poj 1733 经典并查集 附边带权和扩展域两种方法

题目链接:poj 1733

先求一遍前缀和 设每次询问为 l,r,ans  由前缀和的性质我们知道 

如果 ans='odd' 说明 sum[r]和sum[l-1] 的奇偶性不同,否则说明两者奇偶性相同 

第一种方法是边带权,边权d[x]为0,表示x和fa[x]的奇偶性相同,如果d[x]为1,表示x和fa[x]的奇偶性不同 在进行路径压缩时,对x到树根的所有边权做异或运算,可以得到x和树根的关系 

对每个询问,设离散化后l-1,r的值分别是x和y,设ans表示该问题的回答(0代表偶 1代表奇)

  1. 先检查x和y是否在同一个集合内,(奇偶关系是否已知) 如果在同一个集合内 并且d[x]^d[y]!=ans 则该关系和回答矛盾,可以确定撒慌 
  2. 否则合并x和y的两个集合,设两个集合的树根分别为p和q,令fa[p]=q, 已知d[x],d[y]分别表示路径x~p,y~q之间所有边权的"xor和",p~q之间的边权d[p]是待求得值。 显然路径x~y由路径x~p,p~q,q~y组成,因此x与y得奇偶性关系 ans=d[x]^d[y]^d[p] 所以 d[p]=d[x]^d[y]^ans
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 10010;
struct Query{
	int l,r,ans;
}qr[N];
int a[N*2],fa[N*2],d[N*2],n,m,t;
int get(int x){
	if(x==fa[x]) return x;
	int rt=get(fa[x]);d[x]^=d[fa[x]];
	return fa[x]=rt;
}
 
int main(){
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= m; i++){
		char str[5];
		scanf("%d%d%s",&qr[i].l,&qr[i].r,str);
		qr[i].ans=(str[0]=='o'?1:0);
		a[++t]=qr[i].l-1;
		a[++t]=qr[i].r;
	}
	sort(a+1,a+1+t);
	n=unique(a+1,a+1+t)-a-1;
	for(int i = 1; i <= n; i++) fa[i]=i;
	for(int i = 1; i <= m; i++){
		int x = lower_bound(a+1,a+1+n,qr[i].l-1)-a;
		int y = lower_bound(a+1,a+1+n,qr[i].r)-a;
		int p = get(x),q = get(y);
		if(p==q){
			if((d[x]^d[y])!=qr[i].ans){
				printf("%d\n",i-1);
				return 0;
			}
		}else{
			fa[p]=q;d[p]=qr[i].ans^d[x]^d[y];
		} 
	}
	printf("%d\n",m);
	return 0;
}

第二种方法是扩展域的方法,把每个变量x拆成两个结点 x_o_d_d 和 x_e_v_e_n,分别表示前缀和 sum[x]是奇数和sum[x]是偶数,称为奇数域和偶数域 

  1. 如果ans=0,则合并Xodd和Yodd,Xeven和Yeven,这表示x为奇数与y为奇数,x为偶数与y为偶数可以相互推导出
  2. 否则,合并Xodd和Yeven,Xeven和Yodd,这表示x为奇数和y为偶数,x为偶数y为奇数可以相互推导出
  3. 在上述合并前得检查ans是否和已知的信息冲突
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 20010;
struct Query{
	int l,r,ans;
}qr[N];
int a[N*2],fa[N*2],n,m,t;
int get(int x){
	if(x==fa[x]) return x;
	return fa[x]=get(fa[x]);
}
 
int main(){
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= m; i++){
		char str[5];
		scanf("%d%d%s",&qr[i].l,&qr[i].r,str);
		qr[i].ans=(str[0]=='o'?1:0);
		a[++t]=qr[i].l-1;
		a[++t]=qr[i].r;
	}
	sort(a+1,a+1+t);
	n=unique(a+1,a+1+t)-a-1;
	for(int i = 1; i <= 2*n; i++) fa[i]=i;
	for(int i = 1; i <= m; i++){
		int x = lower_bound(a+1,a+1+n,qr[i].l-1)-a;
		int y = lower_bound(a+1,a+1+n,qr[i].r)-a;
		int xo = x,xe = x+n,yo = y,ye = y+n;
		if(qr[i].ans){
			if(get(xo)==get(yo)){
				printf("%d\n",i-1);
				return 0;
			}
			fa[get(xo)]=get(ye),fa[get(xe)]=get(yo);
		}else{
			if(get(xe)==get(yo)){
				printf("%d\n",i-1);
				return 0;
			}
			fa[get(xo)]=get(yo),fa[get(xe)]=get(ye);
		}
	}
	printf("%d\n",m);
	return 0;
}
原创文章 85 获赞 103 访问量 2484

猜你喜欢

转载自blog.csdn.net/weixin_43824564/article/details/105869148