CF903E Bipartite Segments :尺取法

题意

  • 给出区间[l,r],保留区间内的点和点相关联的边。
  • 求子区间[x,y]的数量,满足这个区间的内的点和点组成的边是二分图。

题解

  • 二分图的条件:不含奇圈。题目已经保证不含偶圈,所以要构成二分图,只要不含圈即可。
  • 而且题目隐含不含圈套圈,否则就会产生偶圈。
  • 我们先找到对于点x,找出s[x],表示最大的编号,不含圈,也就是[x,s[x]]区间不含圈。
  • 首先我们要找到圈才行,那就Tarjan一下,找连通分量,然后记录连通分量的最小编号mn[]和最大编号mx[]
  • 那么s[mn[i]] = mx[i] - 1。这样区间[mn[i],mx[i]-1]就不会产生圈了。
  • 然后就是求s[i],s数组从后往前遍历,s[i] = min(s[i],s[i+1])。(自己想想)
  • 最后加入给定区间[l,r],那么我们找一个点p,满足[l,p)区间s[i]<=r,[p,r]区间满足s[i]>=r。那么最后答案就是\sum_{l}^{p-1}(s[i]-l+1) + \sum_{p}^{r}(r -i +1)
  • 预处理前一项的前缀和。

代码

#include <bits/stdc++.h>
using namespace std;
int const N = 300000 + 10;
typedef long long ll;
int n,m,q;
int dfn[N],lowlink[N],cnt,scc,sccno[N];
int mx[N],mn[N],s[N],sz[N];
ll sum[N];
vector<int>G[N];
stack<int>st;
void Tarjan(int u,int fa){
	dfn[u] = lowlink[u] = ++cnt;
	st.push(u);
	for(int i=0;i<G[u].size();i++){          //无向图的连通分量
		int v = G[u][i];
		if(v == fa)	continue;      
		if(!dfn[v]){
			Tarjan(v,u);
			lowlink[u] = min(lowlink[u],lowlink[v]);
		}else if(!sccno[v])	
			lowlink[u] = min(lowlink[u],dfn[v]);
	}
	if(dfn[u] == lowlink[u]){
		scc++;
		while(1){
			sz[scc]++;
			int x = st.top();	st.pop();
			sccno[x] = scc;
			if(x == u)	break;
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	for(int i=1;i<=n;i++)	if(!dfn[i])	Tarjan(i,0);
	for(int i=1;i<=n;i++)	mx[sccno[i]] = i;
	for(int i=n;i>=1;i--)	mn[sccno[i]] = i;
	for(int i=1;i<=n;i++)	s[i] = n;
	for(int i=1;i<=scc;i++)	if(sz[i] > 1)	s[mn[i]] = mx[i] - 1;
	for(int i=n-1;i>=1;i--)	s[i] = min(s[i],s[i+1]);
	for(int i=1;i<=n;i++)	sum[i] = sum[i-1] + s[i] - i + 1;
	scanf("%d",&q);
	for(int i=1;i<=q;i++){
		int l,r;
		scanf("%d%d",&l,&r);
		int p = lower_bound(s+l,s+1+r,r) - s;
		printf("%lld\n",1ll*(r-p+1)*(r-p+2)/2+sum[p-1]-sum[l-1]);
	}
	return 0;
}

 

猜你喜欢

转载自blog.csdn.net/weixin_42264485/article/details/88828419