加权并查集—— uva12232 Exclusive-OR

题目链接:uva12232 Exclusive-OR

分析(by 1879570236):加权并查集,对于每一个节点,记录w[i]等于他与当前father[i]的异或值。

对于Xp^Xq=k合并m和n所在的并查集。合并的话呢:很简单。因为之前做过uva的一道cooprative network,getfather极其类似,getfather的时候更新w[i]。

加一个虚拟节点Xn==0,所以直接告诉Xp=k的话,即Xp^Xn=k。注意一个问题:在合并时,如果一个节点的father是Xn的话无条件把Xn当做合并后的父节点。

对于Q操作的话,把每个w[i]先异或起来,记录他们根节点的访问次数,如果有一个根节点的访问次数为奇数,“不知道”。因为异或不掉这个根节点的值。

另需注意:位运算优先级,打括号!!!

废话不多说,看代码。



分析(by spark):

加权并查集的一般思路 戳这里:并查集【NOI2001 Day1 T3】食物链

考虑与父节点的关系rela[p]表示 x[p]^x[father[p]]

与爷爷节点的关系 显然为 rela[p]^rela[father[p]]

于是操作1很好维护,直接合并集合就可以了。

操作2(x[p]=v)怎么办呢?

维护一个虚拟节点n+1,将操作2视为p与n+1合并,问题就解决了。

对于询问操作:

容易发现: ans= rela[x[p1]]^rela[x[p2]]^......^rela[x[pk]]  ^(所有涉及到的出现了奇数次根节点的值)

注意到只有n+1这个集合中的点的值是已知的,所以其他根节点出现了奇数次,直接输出"I don't konw"

否则输出所有 ans= rela[x[p1]]^rela[x[p2]]^......^rela[x[pk]] 

在代码里是用了一个map来记录出现的集合以及其出现次数的。

有很多细节要注意,所以一开始就想对了,还是wa了6次。

输入的问题是用字符串流解决的,也没有想出更好更快的方法。

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
using namespace std;
int n,q;
struct node{
	char type;
	int k;
	int a,b,v;
	int l[30];
}; 
node work[50005];
int father[50005];
int w[50005];
int getfather(int x){
	if(x!=father[x]){
		int root=getfather(father[x]);
		w[x]^=w[father[x]];
		return father[x]=root;
	}
	else return x;
}
void solve(){
	int i,j,k;
	int cnt=0,ans=0;
	int vis[50005];
	memset(vis,0,sizeof(vis));
	bool flag=true;
	for(i=1;i<=q;i++){
		if(work[i].type=='I'){
			cnt++;
			int fx=getfather(work[i].a);
			int fy=getfather(work[i].b);
			if(fx==n)swap(fx,fy);
			if(fx==fy){
				if((w[work[i].a]^w[work[i].b])!=work[i].v){
					printf("The first %d facts are conflicting.\n",cnt);  
            		return;
				}
			}
			else{
				father[fx]=fy;
				w[fx]=(w[work[i].a]^w[work[i].b]^work[i].v);
			}
		}
		else{
			memset(vis,0,sizeof(vis));
			ans=0;
			flag=true;
			for(j=1;j<=work[i].k;j++){
				int fx=getfather(work[i].l[j]);
				if(fx!=n)vis[fx]^=1;
				ans^=w[work[i].l[j]];
			}
			for(j=1;j<=work[i].k;j++){
				if(vis[father[work[i].l[j]]]){
					flag=false;
				}
			}
			if(flag)printf("%d\n",ans);
			else printf("I don't know.\n");
		}
	}
}
int main(){
	int Case=0;
	while(cin>>n>>q&&n!=0&&q!=0){
		printf("Case %d:\n",++Case); 
		memset(work,0,sizeof(work));
		memset(father,0,sizeof(father));
		memset(w,0,sizeof(w));
		int i,j,k;
		char s[1005];
		for(i=0;i<=n;i++)father[i]=i;
		for(i=1;i<=q;i++){
			int a,b,v;
			scanf("%s",s);
			work[i].type=s[0];
			if(s[0]=='I'){
				gets(s);
				if(sscanf(s,"%d%d%d",&a,&b,&v)==2){
					v=b;
					b=n;
				}
				work[i].a=a;work[i].b=b;work[i].v=v;
			}
			else if(s[0]=='Q'){
				scanf("%d",&work[i].k);
				for(j=1;j<=work[i].k;j++){
					scanf("%d",&work[i].l[j]);
				}
			}
		}
		solve();
		puts("");
	}
}



#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<sstream> 
#include<queue>
#include<map>
#include<algorithm>

#define xx first
#define yy second
#define LL long long
#define CLEAR(xxx) memset(xxx,0,sizeof(xxx))
#define INTpair pair<int,int>

using namespace std;
const int maxn=200000+5,inf=1e9;

int kase,facts,n,m,rela[maxn],fa[maxn];
map<int,int> cnt;
bool conflict;

int getfa(int x){
	if(x==fa[x]) return x;
	int father=fa[x];
	fa[x]=getfa(fa[x]);
	rela[x]^=rela[father];
	return fa[x];
}

void Unoin(int p,int q,int v){   //x[p]^x[q] = v 
	int fp=getfa(p),fq=getfa(q);
	if(fq==n+1) swap(fq,fp);  //注意N+1号节点只能做根节点 
	if(fp==fq&&((rela[p]^rela[q])!=v)) {
		conflict=true ; return ;
	}
	if(fp==fq) return ;
	//Union set p and set q with value v
	fa[fq]=fp;
	rela[fq]=rela[q]^v^rela[p]; 
}

int main(){
	//freopen("ans.out","w",stdout);
	int i,j,k,p,q,v;
	string line;
	char op;
	while(cin>>n>>m&&n&&m){
		for(i=0;i<=n+1;i++)fa[i]=i;
		CLEAR(rela);
		facts=0;conflict=false;
		printf("Case %d:\n",++kase);
		getline(cin,line);
		for(i=1;i<=m;i++){
			getline(cin,line);
			if(conflict) continue;
			stringstream ss(line);
			ss>>op;
			if(op=='I'){
				facts++;
				ss>>p>>v;
				if(ss>>q) Unoin(p,v,q);
				else Unoin(n+1,p,v);
			}
			else {
				cnt.clear();  
				int ans=0,ok=1;
				ss>>k;
				for(j=1;j<=k;j++){
					ss>>v;
					cnt[getfa(v)]++;
					ans^=rela[v];
				}
				map<int,int>:: iterator it;
				for(it=cnt.begin();it!=cnt.end();it++)
					if(it->xx!=n+1&&it->yy%2==1){
						ok=0; break;
					}
				if(ok)cout<<ans<<endl;
				else puts("I don't know.");
			}
			if(conflict)
				printf("The first %d facts are conflicting.\n",facts);
		}
		puts("");
	}
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值