Codeforeces

来源:互联网 发布:如何开发一门编程语言 编辑:程序博客网 时间:2024/05/22 06:56

E. Bipartite Segments

分类: data structures dfs and similar

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

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