[CF901C] Bipartite Segments

题目大意

给你一个有(n)个点的无向图,没有偶环。我们把节点标记为(1..n)

你需要回答(q)个询问,每一个询问由一个区间[L,R](1<=L<=R<=n)组成,你需要计算出有多少个点对[x,y](L<=x<=y<=R),满足由[x,y]之间的所有点组成的子图是一个二分图。

输入数据第一行两个整数 (n,m (1le nle 3 imes 10^5,1le mle 3 imes10^5))代表点数和边数 接下来的(m)行,每行两个整数,表示一条无向边,保证无自环,保证无重边

一个整数 (q(1le qle3 imes 10^5)),表示询问个数

接下来q行,每行一个询问[L,R]

对于每个询问,输出一个整数,表示满足条件的点对[x,y]的个数

解析

首先想到的一定是”没有偶环“这一特殊的性质。仔细思考一下,没有偶环,也就等价于没有环套环的情况,因为如果有两个环套在一起,如果两个简单环都是奇环,那么一定会产生一个偶环(具体证明可以自行解决)。那么,原图就是一个仙人掌,并且所有环都是奇环。

接下来考虑询问。二分图的条件是没有奇环,那么满足要求的区间就不能包括任何一个环。因此,首先用Tarjan求边双连通分量,把所有环找出来,记第(i)个环中最小的点为(L[i]),最大的点为(R[i]),那么对于一个区间([1,L[i]])中的点,以它为左端点的区间一定不能覆盖(R[i])及其以后的点。所以,我们可对每个点(i)求出它最远能够覆盖到的点(nxt[i])(线段树区间修改最小值),对于一个询问区间([l,r]),二分找到满足(nxt[i]<=r)的最右边的点,该点左边的用(nxt)统计答案((sum nxt[i]-i)),右边的所有子区间都满足条件,相加即为询问的答案。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long
#define N 300002
using namespace std;
const int S=600001;
const int inf=1<<30;
struct SegmentTree{
	int dat,add;
}t[N*4];
int head[N],ver[N*2],nxt[N*2],l;
int n,m,q,i,tim,dfn[N],low[N],top,s[N],cnt,scc[N],L[N],R[N],a[N],sum[N];
bool cut[N*2];
int read()
{
	char c=getchar();
	int w=0;
	while(c<'0'||c>'9') c=getchar();
	while(c<='9'&&c>='0'){
		w=w*10+c-'0';
		c=getchar();
	}
	return w;
}
void insert(int x,int y)
{
	ver[l]=y;
	nxt[l]=head[x];
	head[x]=l;
	l++;
}
void Tarjan(int x,int pre)
{
	dfn[x]=low[x]=++tim;
	for(int i=head[x];i!=-1;i=nxt[i]){
		int y=ver[i];
		if(!dfn[y]){
			Tarjan(y,i);
			low[x]=min(low[x],low[y]);
			if(low[y]>dfn[x]) cut[i]=cut[i^1]=1;
		}
		else if(i!=(pre^1)) low[x]=min(low[x],dfn[y]);
	}
}
void dfs(int x)
{
	scc[x]=cnt;
	L[cnt]=min(L[cnt],x);
	R[cnt]=max(R[cnt],x);
	for(int i=head[x];i!=-1;i=nxt[i]){
		if(cut[i]) continue;
		int y=ver[i];
		if(!scc[y]) dfs(y);
	}
}
void update(int p)
{
	t[p].dat=min(t[p*2].dat,t[p*2+1].dat);
}
void spread(int p)
{
	if(t[p].add!=inf){
		t[p*2].add=min(t[p*2].add,t[p].add);
		t[p*2+1].add=min(t[p*2+1].add,t[p].add);
		t[p*2].dat=min(t[p*2].dat,t[p].add);
		t[p*2+1].dat=min(t[p*2+1].dat,t[p].add);
		t[p].add=inf;
	}
}
void build(int p,int l,int r)
{
	t[p].add=t[p].dat=inf;
	if(l==r) return;
	int mid=(l+r)/2;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
}
void change(int p,int l,int r,int ql,int qr,int x)
{
	if(ql<=l&&r<=qr){
		t[p].dat=min(t[p].dat,x);
		t[p].add=min(t[p].add,x);
		return;
	}
	int mid=(l+r)/2;
	spread(p);
	if(ql<=mid) change(p*2,l,mid,ql,qr,x);
	if(qr>mid) change(p*2+1,mid+1,r,ql,qr,x);
	update(p);
}
int ask(int p,int l,int r,int x)
{
	if(l==r) return t[p].dat;
	int mid=(l+r)/2;
	spread(p);
	if(x<=mid) return ask(p*2,l,mid,x);
	return ask(p*2+1,mid+1,r,x);
}
signed main()
{
	memset(head,-1,sizeof(head));
	memset(L,0x3f,sizeof(L));
	n=read();m=read();
	for(i=1;i<=m;i++){
		int u=read(),v=read();
		insert(u,v);
		insert(v,u);
	}
	for(i=1;i<=n;i++){
		if(!dfn[i]) Tarjan(i,S);
	}
	for(i=1;i<=n;i++){
		if(!scc[i]) cnt++,dfs(i);
	}
	build(1,1,n);
	for(i=1;i<=cnt;i++){
		if(R[i]!=L[i]) change(1,1,n,1,L[i],R[i]);
	}
	for(i=1;i<=n;i++){
		a[i]=ask(1,1,n,i);
		sum[i]=sum[i-1]+a[i]-i;
	}
	q=read();
	for(i=1;i<=q;i++){
		int x=read(),y=read();
		int p=upper_bound(a+x,a+y+1,y)-a-1;
		int num1=sum[p]-sum[x-1],num2=y-p;
		printf("%lld
",num1+num2*(num2+1)/2);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/LSlzf/p/12196272.html