并查集题集
来源:互联网 发布:数据魔方是什么 编辑:程序博客网 时间:2024/06/10 13:26
POJ-1182
将所有有关系的动物放到并查集中。维护一个带权并查集。
每个点带的权值是
偏移量为0表示同类,为1表示它吃它父亲,为2表示它父亲吃它,这样当偏移量不对的时候就可以判定是假话了。
#include<cstdio>using namespace std;const int N=50007;int fa[N],off[N],n;void init(){ for(int i=1;i<=n;++i) fa[i]=i,off[i]=0;}int f(int x,int &d){ if(x==fa[x]) { d=0; return x; } fa[x]=f(fa[x],d); d=(d+off[x])%3; off[x]=d; return fa[x];}//x与y的偏移量之差为0表示同类,为1表示吃y,为2表示被y吃int main(){ int m; scanf("%d%d",&n,&m); int ans=0,deep; init(); while(m--) { int d,x,y; scanf("%d%d%d",&d,&x,&y); if(x>n||y>n) ++ans; else if(d==2&&x==y) ++ans; else { --d; int rtx=f(x,deep); int rty=f(y,deep); if(rtx==rty) { if((off[x]-off[y]+3)%3!=d) ++ans; } else { fa[rtx]=rty; off[rtx]=(off[rty]+d-off[x]+off[y]+3)%3; } } } printf("%d\n",ans); return 0;}
HDU-3038
由于区间和可以表示前缀和相减的形式,因此我们维护前缀和之间的关系。若前缀和之间有关系,且与输入的不一样,那么++ans
就行了
#include<bits/stdc++.h>using namespace std;const int N=2e5+7;int n,fa[N],p[N];void init() { for(int i=0;i<=n;++i) fa[i]=i,p[i]=0; }int f(int x){ if(x==fa[x]) return x; int father=fa[x]; fa[x]=f(fa[x]); p[x]+=p[father]; return fa[x];}int main(){ int m; while(~scanf("%d%d",&n,&m)) { int ans=0; init(); while(m--) { int l,r,d; scanf("%d%d%d",&l,&r,&d); --l; int rtl=f(l); int rtr=f(r); if(rtl!=rtr) { p[rtr]=d-p[r]+p[l]; fa[rtr]=rtl; } else if(p[r]-p[l]!=d) ++ans; } printf("%d\n",ans); } return 0;}
POJ-1417
如果答案为 yes ,则x和y种类相同,否则x和y种类不同,用并查集维护,最后问题就转化成背包了。
注意数据 0 1 0
和 0 0 1
。
#include<cstdio>#include<cstring>#include<vector>#include<algorithm>using namespace std;const int N=607;int fa[N],st[N],p1,p2,c[2][N],dp[N][2][N],pre[N][2][N];vector<int> p[2][N],ans;void init(){ memset(c,0,sizeof(c)); ans.clear(); for(int i=1;i<=p1+p2;++i) { fa[i]=i; st[i]=0; p[0][i].clear(); p[1][i].clear(); }}int f(int x){ if(x!=fa[x]) { int father=fa[x]; fa[x]=f(fa[x]); st[x]=(st[x]+st[father])&1; } return fa[x];}int main(){ int q; while(~scanf("%d%d%d",&q,&p1,&p2)) { if(q==0&&p1==0&&p2==0) break; char s[10]; int x,y; init(); while(q--) { scanf("%d%d%s",&x,&y,s); int rtx=f(x),rty=f(y); if(rtx==rty) continue; st[rtx]=(st[y]-st[x]+(s[0]=='n')+2)&1; fa[rtx]=rty; } for(int i=1;i<=p1+p2;++i) { int rt=f(i); c[st[i]][rt]++; p[st[i]][rt].push_back(i); } int n=0; for(int i=1;i<=p1+p2;++i) if(c[0][i]||c[1][i]) { ++n; c[0][n]=c[0][i]; c[1][n]=c[1][i]; p[0][n]=p[0][i]; p[1][n]=p[1][i]; } memset(dp,0,sizeof(dp)); dp[0][0][0]=1; for(int i=1;i<=n;++i) { for(int k=0;k<2;++k) { for(int j=p1;j>=c[k][i];--j) { if(dp[i-1][0][j-c[k][i]]) dp[i][k][j]+=dp[i-1][0][j-c[k][i]],pre[i][k][j]=0; if(dp[i-1][1][j-c[k][i]]) dp[i][k][j]+=dp[i-1][1][j-c[k][i]],pre[i][k][j]=1; } } } if(dp[n][0][p1]+dp[n][1][p1]==1) { int t=(dp[n][1][p1]==1); for(int i=n;i>=1;--i) { for(int j=0;j<p[t][i].size();++j) ans.push_back(p[t][i][j]); int ppp=p1; p1-=c[t][i]; t=pre[i][t][ppp]; } } sort(ans.begin(),ans.end()); for(int i=0;i<ans.size();++i) printf("%d\n",ans[i]); if(ans.size()||p1==0) puts("end"); else puts("no"); } return 0;}
POJ-1733
带权并查集维护前缀和的奇偶性是否相同就行了
#include<algorithm>#include<cstdio>using namespace std;const int N=10007;int fa[N],off[N],L[N],R[N],b[N],op[N];void init(int n){ for(int i=0;i<n;++i) fa[i]=i,off[i]=0;}int f(int x){ if(x!=fa[x]) { int father=fa[x]; fa[x]=f(fa[x]); off[x]=(off[x]+off[father])&1; } return fa[x];}int main(){ int n,q; while(~scanf("%d",&n)) { scanf("%d",&q); char s[10]; int m=0; for(int i=0;i<q;++i) { scanf("%d%d%s",&L[i],&R[i],s); b[m++]=--L[i]; b[m++]=R[i]; op[i]=(s[0]=='o'); } sort(b,b+m); m=unique(b,b+m)-b; init(m); int ans=q; for(int i=0;i<q;++i) { int l=lower_bound(b,b+m,L[i])-b; int r=lower_bound(b,b+m,R[i])-b; int rtl=f(l); int rtr=f(r); if(rtl==rtr) { if(((off[r]-off[l]+2)&1)^op[i]) { ans=i; break; } } else { fa[rtr]=fa[rtl]; off[rtr]=(op[i]+off[l]-off[r]+2)&1; } } printf("%d\n",ans); } return 0;}
POJ - 1984
带权并查集维护点之间x坐标与y坐标之差就行了。
#include<cstdio>#include<cmath>#include<vector>using namespace std;const int N=4e4+7;int dir[4][2] = { {0,1},{0,-1},{1,0},{-1,0} };int f1[N],f2[N],L[N],d[N],ans[N];struct Query{ int a,b,id; Query(int a,int b,int id) : a(a) , b(b) , id(id) {} Query(){}};vector<Query> q[N];int fa[N],x[N],y[N];int Abs(int x) { return x>0?x:-x; }void init(int n){ for(int i=1;i<=n;++i) { fa[i]=i; x[i]=y[i]=0; }}int f(int k){ if(k!=fa[k]) { int father=fa[k]; fa[k]=f(fa[k]); x[k]+=x[father]; y[k]+=y[father]; } return fa[k];}int main(){ int n,m,k; while(~scanf("%d%d",&n,&m)) { char s[10]; init(n); for(int i=1;i<=m;++i) q[i].clear(); for(int i=1;i<=m;++i) { scanf("%d%d%d%s",&f1[i],&f2[i],&L[i],&s); if(s[0]=='E') d[i]=2; else if(s[0]=='W') d[i]=3; else if(s[0]=='N') d[i]=0; else d[i]=1; } scanf("%d",&k); for(int i=0;i<k;++i) { int a,b,t; scanf("%d%d%d",&a,&b,&t); q[t].push_back(Query(a,b,i)); } for(int i=1;i<=m;++i) { int l=f1[i],r=f2[i]; int rtl=f(l); int rtr=f(r); if(rtl!=rtr) { fa[rtr]=rtl; x[rtr]=(L[i]*dir[d[i]][0]+x[l]-x[r]); y[rtr]=(L[i]*dir[d[i]][1]+y[l]-y[r]);// printf("%d %d\n",x[rtr],y[rtr]); } for(int j=0;j<q[i].size();++j) { int a=q[i][j].a,b=q[i][j].b; int rta=f(a); int rtb=f(b); if(rta!=rtb) ans[q[i][j].id]=-1; else ans[q[i][j].id]=Abs(x[a]-x[b])+Abs(y[a]-y[b]); } } for(int i=0;i<k;++i) printf("%d\n",ans[i]); } return 0;}
POJ - 2492
这题可以用带权并查集做也可以用 u - v + n 表示不同性,u - v 表示同性这种方法来做。
#include<cstdio>using namespace std;const int N=4007;int fa[N];inline void init(int n){ for(int i=1;i<=n;++i) fa[i]=i; }int f(int x) { return x==fa[x]?x:fa[x]=f(fa[x]); }inline void Union(int x,int y ){ x=f(x);y=f(y);fa[x]=y; }int main(){ int T,kase=1; scanf("%d",&T); while(T--) { int n,m; scanf("%d%d",&n,&m); init(n<<1); bool ok=true; while(m--) { int u,v; scanf("%d%d",&u,&v); if(!ok) continue; if(f(u)==f(v)) ok=false; Union(u,v+n); Union(u+n,v); } printf("Scenario #%d:\n%s\n\n",kase++,ok?"No suspicious bugs found!":"Suspicious bugs found!"); } return 0;}
POJ-2912
一开始想着直接并查集,找出两个反例就输出裁判,最后还玄学特判了好多。。。还是too naive 。。 只要枚举每个裁判跑并查集就行了。
如果有唯一的裁判,那判断他的步数是使得所有其他人都不可能是裁判的最小的步数,其他情况按题意就行。
#include<cstdio>#include<algorithm>#include<set>#include<cctype>using namespace std;const int N=507;char s[100];int fa[N],off[N],L[N<<2],R[N<<2],OP[N<<2];void sep(int &u,int &v,int mid){ u=v=0; for(int i=0;i<mid;++i) u=u*10+(s[i]-'0'); for(int i=mid+1;s[i]!='\0';++i) v=v*10+(s[i]-'0');}void init(int n){ for(int i=0;i<n;++i) fa[i]=i,off[i]=0;}int f(int x){ if(x!=fa[x]) { int father=fa[x]; fa[x]=f(fa[x]); off[x]=(off[father]+off[x])%3; } return fa[x];}int main(){ int n,m; while(~scanf("%d%d",&n,&m)) { for(int i=1;i<=m;++i) { scanf("%s",s); for(int j=0;;++j) { if(!isdigit(s[j])) { sep(L[i],R[i],j); OP[i]=s[j]=='='?0:(s[j]=='>'?1:2); break; } } } int c=0,ans,tm=0; for(int judge=0;judge<n;judge++) { init(n); bool ok=true; for(int i=1;i<=m;++i) { int x=L[i],y=R[i],op=OP[i]; if(x==judge||y==judge) continue; int rtx=f(x); int rty=f(y); if(rtx!=rty) { off[rtx]=(op+off[y]-off[x]+3)%3; fa[rtx]=rty; } else if((off[x]-off[y]+3)%3!=op) { tm=max(i,tm); ok=false; break; } } if(ok) ++c,ans=judge; } if(c==1) printf("Player %d can be determined to be the judge after %d lines\n",ans,tm); else if(c==0) puts("Impossible"); else puts("Can not determine"); } return 0;}
HDU-1272
水
#include<bits/stdc++.h>using namespace std;const int N=1e5+7;int fa[N],n;bool vis[N];void init(){ for(int i=1;i<N;++i) fa[i]=i,vis[i]=0; }int f(int x) { return x==fa[x]?x:fa[x]=f(fa[x]); }void Union(int x,int y){ x=f(x);y=f(y);fa[x]=y; }void solve(int u,int v){ if(!vis[u]) vis[u]=true,++n; if(!vis[v]) vis[v]=true,++n;}int main(){ int u,v,m; while(~scanf("%d%d",&u,&v)) { if(u==-1&&v==-1) break; if(u==0&&v==0) { puts("Yes"); continue; } bool ok=true; m=n=0; init(); ++m; if(f(u)==f(v)) ok=false; Union(u,v); solve(u,v); while(~scanf("%d%d",&u,&v)) { if(u==0&&v==0) break; ++m; if(f(u)==f(v)) ok=false; solve(u,v); Union(u,v); } if(n!=m+1) ok=false; puts(ok?"Yes":"No"); } return 0;}
ZOJ-3261
倒着。。并查集
#include<bits/stdc++.h>using namespace std;const int N=1e4+7;typedef pair<int,int> pii;int p[N],op[N*5],A[N*5],B[N*5],fa[N],id[N],mx[N],ans[N*5];set<pii> s;void init(int n){ s.clear(); for(int i=0;i<n;++i) { fa[i]=i; mx[i]=p[i]; id[i]=i; }}int f(int x) { return x==fa[x]?x:fa[x]=f(fa[x]); }void Union(int x,int y){ x=f(x); y=f(y); if(mx[x]>mx[y]) { mx[y]=mx[x]; id[y]=id[x]; } else if(mx[x]==mx[y]) id[y]=min(id[y],id[x]); fa[x]=y;}int main(){ int n; bool first=true; while(~scanf("%d",&n)) { if(first) first=false; else puts(""); int m,q; for(int i=0;i<n;++i) scanf("%d",&p[i]); init(n); scanf("%d",&m); for(int i=0;i<m;++i) { int u,v; scanf("%d%d",&u,&v); s.insert(make_pair(min(u,v),max(u,v))); } scanf("%d",&q); char str[100]; int a,b; for(int i=0;i<q;++i) { scanf("%s%d",str,&a); if(str[0]=='q') op[i]=0,A[i]=a; else { scanf("%d",&b); op[i]=1; A[i]=a;B[i]=b; s.erase(make_pair(min(a,b),max(a,b))); } } for(set<pii>::iterator it=s.begin();it!=s.end();it++) Union(it->first,it->second); int c=0; for(int i=q-1;i>=0;--i) { int a=A[i],b=B[i]; if(op[i]) Union(a,b); else { if(mx[f(a)]>p[a]) ans[c++]=id[f(a)]; else ans[c++]=-1; } } for(int i=c-1;i>=0;--i) printf("%d\n",ans[i]); } return 0;}
HDU-6109
需要判定两种矛盾的情况:
- 之前相等,出现不等。可以直接有并查集维护,在同一个集合中则说明相等。
- 之前不等,出现相等。可以用set维护与当前集合不等的所有元素,合并时将小集合并到大集合复杂度是
O(nlogn) 。
#include<bits/stdc++.h>using namespace std;const int N=1e5+7;set<int> s[N];int fa[N],ans[N],a[N],b[N],c[N];void init(int l,int r){ for(int i=l;i<=r;++i) { fa[a[i]]=a[i]; fa[b[i]]=b[i]; s[a[i]].clear(); s[b[i]].clear(); }}int f(int x) { return x==fa[x]?x:fa[x]=f(fa[x]); }void Union(int x,int y){ x=f(x);y=f(y); if(s[x].size()<s[y].size()) swap(x,y); fa[y]=x; for(int v : s[y]) s[x].insert(v); s[y].clear();}int main(){ int n,m=0; scanf("%d",&n); ans[0]=0; for(int i=0;i<N;++i) fa[i]=i; for(int i=1;i<=n;++i) scanf("%d%d%d",&a[i],&b[i],&c[i]); for(int i=1;i<=n;++i) { int x=f(a[i]); int y=f(b[i]); if(c[i]) { if(x==y) continue; else if(s[x].count(b[i])) { ans[++m]=i; init(ans[m-1]+1,ans[m]); } else Union(x,y); } else { if(x==y) { ans[++m]=i; init(ans[m-1]+1,ans[m]); } else { s[x].insert(b[i]); s[y].insert(a[i]); } } } printf("%d\n",m); for(int i=1;i<=m;++i) printf("%d\n",ans[i]-ans[i-1]); return 0;}
阅读全文
0 0
- 并查集题集
- 并查集题集(一)
- 并查集三
- 并查集刷题
- 并查
- 并查集略解
- 并查集小练
- 并发网
- 矩形 面积并 && 周长并
- 串并并串转换
- HDU1232 并查集<并>
- 并集查找以及和并
- HDU3938 并查集 并查集
- 【并查集】-HDU1213-赤裸并查最水
- 【并查集】SOJ并查集篇
- js2个数组合并并去重
- 并查集学习并实现
- 并查集(集并查)
- 使用sqoop导入导出数据
- 一些有用的三维点云数据集网站
- Docker Registry服务器部署配置
- GeekBand笔记-《C++设计模式》 第三周
- firewall,iptablse和selinux防火墙
- 并查集题集
- (M)Backtracking:526. Beautiful Arrangement
- Windows 上好用的7款下载工具
- 顺序表应用6:有序顺序表查询
- openssh-server
- 多媒体之使用PlaySound播放音频
- 重拾Java--功底篇之HashMap
- 第二章 Linux安装课后习题
- 线段树题集