• Codeforces 901C Bipartite Segments(Tarjan + 二分)


    题目链接  Bipartite Segments

    题意  给出一个无偶环的图,现在有$q$个询问。求区间$[L, R]$中有多少个子区间$[l, r]$

      满足$L <= l <= r <= R$,并且一个只包含$l$到$r$这些点的无向图为二分图。

    因为整张图没有偶环,所以在这道题中如果某个无向图没有环,那个这个无向图就是二分图

    Tarjan求出每个环的标号最小点和标号最大点。

    令$f[i]$为能保证$[i, j]$这个区间构成的图都是二分图的$j$的最大值,则$f[i]$是不下降的

    当询问区间$[L, R]$的时候,即求$egin{equation*}sum_{i=L}^Rmin(f(i), R) - i + 1end{equation*}$

    二分然后分类讨论即可

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define rep(i, a, b)	for (int i(a); i <= (b); ++i)
    #define dec(i, a, b)	for (int i(a); i >= (b); --i)
    
    typedef long long LL;
    
    const int N = 3e5 + 10;
    
    int f[N], dfn[N], stk[N];
    int n, m, ti = 0;
    int q, top;
    LL s[N];
    vector <int> v[N];
    
    void dfs(int x, int fa){
    	dfn[x] = ++ti;
    	stk[++top] = x;
    	for (auto u : v[x]){
    		if (u == fa) continue;
    		if (dfn[u]){
    			if (dfn[u] < dfn[x]){
    				int mx = u, mi = u;
    				for (int p = top; stk[p] != u; --p){
    					mi = min(mi, stk[p]);
    					mx = max(mx, stk[p]);
    				}
    				f[mi] = mx;
    			}
    		}
    		else dfs(u, x);
    	}
    	--top;
    }
    
    int main(){
    
    	scanf("%d%d", &n, &m);
    	rep(i, 1, m){
    		int x, y;
    		scanf("%d%d", &x, &y);
    		v[x].push_back(y);
    		v[y].push_back(x);
    	}
    
    	rep(i, 1, n) if (!dfn[i]) dfs(i, 0);
    	rep(i, 1, n) if (f[i]) --f[i]; else f[i] = n;
    	dec(i, n - 1, 1) f[i] = min(f[i], f[i + 1]);
    	rep(i, 1, n) s[i] = s[i - 1] + f[i];
    
    	scanf("%d", &q);
    	while (q--){
    		int x, y;
    		scanf("%d%d", &x, &y);
    		if (f[x] > y){
    			LL num = y - x + 1;
    			printf("%lld
    ", 0ll - 1ll * (x + y) * num / 2 + 1ll * y * num + num);
    			continue;
    		}
    
    		int l = x, r = y;
    		while (l + 1 < r){
    			int mid = (l + r) >> 1;
    			if (f[mid] <= y) l = mid; else r = mid - 1;
    		}
    
    		int t;
    		if (f[r] <= y) t = r; else t = l;
    
    		int u = t + 1;
    		LL num = y - x + 1;
    		LL ans = num - 1ll * (x + y) * num / 2;
    
    		LL et = y - u + 1;
    		ans = ans + (s[t] - s[x - 1]);
    		if (et > 0) ans = ans + 1ll * y * et;
    		printf("%lld
    ", ans);
    
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    最小生成树+BFS J
    Noip 2016
    舒适的路线 (code[vs] 1001)
    拦截导弹 (加了神奇的位运算)
    逃出克隆岛 (codevs 2059)
    回家(洛谷 P1592)
    热浪
    城堡
    笔记 (一道正解思路巧妙的题)
    脱水缩合
  • 原文地址:https://www.cnblogs.com/cxhscst2/p/8277411.html
Copyright © 2020-2023  润新知