分析(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;
}