Codeforeces - 903E. Bipartite Segments - dfs、二分图

E. Bipartite Segments
分类: data structures dfs and similar

题意: 给你一个无向图,n(1≤n≤3×105) 个点,保证没有偶数环,有 q(1≤q≤3×105) 次询问,每次给出 l r, 问只保留编号 [l,r] 区间的点的边,有多少个区间内点和它们的边构成的图是二分图?

解题思路: 判断一个无向图是否是二分图的重要性质就是——没有奇数度的环!因此,这样考虑,预处理出所有环(因为已经保证了没有偶数度环)的最小编号和最大编号,这个做法很多,类似tarjan的方法是考虑用栈存一下当前的访问的编号,当访问到已经标记的点,则一直退栈同时更新最大值最小值(因为环内元素就是一个双连通分量!),对于每个查询,如果它不包含任何奇数度环,那么答案就是 ∑i=(r−i+1)=(r−l+1)·(r−l+2)/2而如果包含了奇数度环的话,考虑所有包含环的最右边的位置 p(p≤r) ,显然在 [p+1,r] (如果 p+1≤r )的答案就是前者的情况,而在 [l,p] 则重新考虑。又想,对于每个点 i 维护一个区间 [i,pi] 表示 i 和 i+1到 pi之间的点都能构成二分图(这是常用对于每个点满足的性质向两边扩展的贪心思维),那么对于点 ii 为左端点能够构成的二分图个数就是 (pi−i+1) 个!记为 qi,那么结论就来了,对于区间 [l,p] 答案就是 ∑i=qi,维护这个区间和可以用线段树…最简单的做法就是维护关于 qi的后缀和!下面只剩如何求 p 啦,而对于区间 [l,r] ,包含环的最右边位置 p 就是 max{pi}, l≤i,pi≤r,可以用线段树等数据结构维护,而且容易发现, pi具有单调性,对于位置 x 如果 px>r 那么 x+1 位置 px+1>r 也显然成立!(根据刚才定义!)那么有一种更简单地写法是直接在区间 [l,r] 内二分 p 的位置!剩下的都是细节了,以上。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 10;
vector<int> g[maxn];
int vis[maxn];
stack<int> S;
int p[maxn];
ll sum[maxn];
void dfs(int u, int fa) {
    S.push(u);
    vis[u] = 1;
    for (int i = 0; i < g[u].size(); i++) {
        int v = g[u][i];
        if (v != fa) {
            if (vis[v] == 0) dfs(v, u);
            else if (vis[v] == 1) {
                int Max = u, Min = u;
                while (!S.empty()) {
                    int w = S.top(); S.pop();
                    Max = max(Max, w); Min = min(Min, w);
                    if (w == v) break;
                }
//                printf("<%d %d\n", Min, Max);
                p[Min] = Max;
            }
        }
    }
    if (!S.empty() && S.top() == u) S.pop();
    vis[u] = 2;
}
int main() {
    int n, m, q;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) {
        int a, b;
        scanf("%d%d", &a, &b);
        g[a].push_back(b);
        g[b].push_back(a);
    }
    memset(vis, 0, sizeof vis);
    for (int i = 1; i <= n + 1; i++) p[i] = n + 1;
    for (int i = 1; i <= n; i++) {
        if (vis[i] == 0) dfs(i, -1);
    }
    sum[n + 1] = 0;
    for (int i = n; i >= 1; i--) {
        p[i] = min(p[i], p[i + 1]);
        sum[i] = p[i] - i + sum[i + 1];
//        printf("p[%d] %d - sum[%d] %I64d\n", i, p[i], i, sum[i]);
    }
    scanf("%d", &q);
    while (q--) {
        int l, r;
        scanf("%d%d", &l, &r);
        int L = l, R = r, P = l;
        while (L <= R) {
            int m = L + R >> 1;
            if (p[m] <= r) L = m + 1;
            else {
                P = m;
                R = m - 1;
            }
        }
        printf("%I64d\n", sum[l] - sum[P] + 1LL * (r - P + 2) * (r - P + 1) / 2);
    }
    return 0;
}


--------------------- 
作者:寒江雪里独钓着的蓑笠翁 
来源:CSDN 
原文:https://blog.csdn.net/dragon60066/article/details/78854551 
 

猜你喜欢

转载自blog.csdn.net/c_czl/article/details/88167291