【gmoj】【线段数】 【并查集】 graph

14 篇文章 0 订阅
12 篇文章 0 订阅

【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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值