题目大意
给你一个有\(n\)个点的无向图,没有偶环。我们把节点标记为\(1..n\)。
你需要回答\(q\)个询问,每一个询问由一个区间[L,R](1<=L<=R<=n)
组成,你需要计算出有多少个点对[x,y](L<=x<=y<=R)
,满足由[x,y]
之间的所有点组成的子图是一个二分图。
输入数据第一行两个整数 \(n,m (1\le n\le 3\times 10^5,1\le m\le 3\times10^5)\)代表点数和边数 接下来的\(m\)行,每行两个整数,表示一条无向边,保证无自环,保证无重边
一个整数 \(q(1\le q\le3\times 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\n",num1+num2*(num2+1)/2);
}
return 0;
}