加权并查集—— uva12232 Exclusive-OR
2016-09-02 23:13
197 查看
题目链接: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("");
}
}
分析(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; }
相关文章推荐
- HDU 3234 | UValive 4487 - Exclusive-OR (加权并查集)
- uva12232 - Exclusive-OR 加权并查集
- 并查集(加权) LA 4487 Exclusive-OR
- UVALive 4487 Exclusive-OR 加权并查集神题
- LA4487 Exclusive-OR (加权并查集)
- UVALIVE 4487 Exclusive-OR(加权并查集)
- uva 12232 - Exclusive-OR(加权并查集)
- UVA - 1329 Corporative Network(加权并查集)
- BZOJ[4602] [Sdoi2016]齿轮 加权并查集
- 叠积木(加权并查集)
- HDU 3234 Exclusive-OR(加权并差集)
- HDU3234Exclusive-OR(并查集)与HDU3038相似
- <(扩展域/加权)并查集>NOI 2001 食物链
- UVA12232加权并查集 加点
- HDU3234&&UVA12232&&LA4487:Exclusive-OR(经典带权并查集)
- 【BZOJ 1202】 [HNOI2005]狡猾的商人 (加权并查集)
- [BZOJ4602][Sdoi2016]齿轮(加权并查集)
- 【HDU5652 BestCoder Round 77 (div1) B】【并查集 or 二分答案BFS】India and China Origins 两国中间出现山脉 最早时间断绝联系
- uva 11987 Almost Union-Find(加权并查集)
- UVALive 4487 - Exclusive-OR -并查集 虚根