【gmoj】【线段数】 【并查集】 graph
题目
解题思路
因为有减边的操作
所以我们可以将每条边按存在的时间丢进线段树中维护
用按秩并查集维护边
二分图如果存在奇环就不成立
如果一条边的两端不在同一集合就合并,放进栈中
如果在同一集合且是奇环就直接输出NO
小小优化,如果当前已经是奇环,那可以不用往下了,输出 l~r 的NO
跑完后要弹出栈
代码
#include<iostream>
#include<cstdio>
using namespace std;
const int N=300010;
struct lzf{
int x,to,be,end;
}f[N];
struct yty{
int x,fr;
}e[10*N];
struct node{
int fy,l,fx;
}zc[5*N];
int n,m,t,cnt,w,x,y,num;
int fa[N],val[N],len[N],s[10*N];
void put(int x,int l,int r,int line) //丢进线段树维护
{
if (f[line].be<=l&&f[line].end>=r)
{
e[++cnt]=(yty){line,s[x]};
s[x]=cnt; //s维护的是这个时间点的上一条边
return;
}
int mid=(l+r)/2,ans;
if (f[line].be<=mid) put(x*2,l,mid,line);
if (f[line].end>mid) put(x*2+1,mid+1,r,line);
}
void solve(int x,int l,int r)
{
int num2=num,vis=0;
for (int i=s[x];i;i=e[i].fr)
{
int v=e[i].x,fx=f[v].x,fy=f[v].to,ans=1;
while (fa[fx]) ans=ans^val[fx],fx=fa[fx];
while (fa[fy]) ans=ans^val[fy],fy=fa[fy]; //ans是判断是否是奇环,奇+奇是偶,偶加偶是偶,只有奇+偶是奇,用异或
if (fx!=fy)
{
if (len[fx]>len[fy]) swap(fx,fy);
fa[fx]=fy,val[fx]=ans; //fa维护父亲,val维护节点数的奇偶情况
zc[++num]=(node){fy,len[fy],fx};
len[fy]=max(len[fy],len[fx]+1); //len维护深度
}
else if (ans)
{
for (int j=l;j<=r;j++) printf("NO\n");
vis=1;
break;
}
}
if (!vis)
if (l==r) printf("YES\n");
else {
int mid=(l+r)/2;
solve(x*2,l,mid);
solve(x*2+1,mid+1,r);
}
while (num>num2) //弹出栈,类似dfs的回溯
{
len[zc[num].fy]=zc[num].l;
fa[zc[num].fx]=val[zc[num].fx]=0;
num--;
}
}
int main()
{
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&w,&x);
x++;
if (w==0)
f[x].end=i-1;
else
{
scanf("%d",&y),y++;
f[++t]=(lzf){x,y,i,m};
}
}
for (int i=1;i<=t;i++)
put(1,1,m,i);
solve(1,1,m);
return 0;
}