1、模板题 poj1611the suspects
每个组内的人,同一个组内都是感染者,问与“0”号人有关的有多少人
#include <iostream> #include<cstdio> using namespace std; const int MAXN = 1000100; struct DS { int f[MAXN]; void init(int n) { for(int i=0;i<n;i++) f[i]=i; } int ff(int x) ///int father (a); { ///查找父亲并压缩路径 if(f[x]!=x) f[x]=ff(f[x]); return f[x]; } void join(int a,int b) ///void union (a, b); { f[ff(a)]=f[ff(b)]; } int find(int a,int b) { return ff(a)==ff(b); } }soul; int main() { // freopen("cin.txt","r",stdin); int n,m,a,b,c,t,num[100000]; while(cin>>n>>m) ///n:人数 m:操作数 { if(m==0&&n==0) break; soul.init(n); while(m--) { cin>>t; for(int i=1;i<=t;i++) cin>>num[i]; for(int i=1;i<t;i++) soul.join(num[i],num[i+1]); } int q=soul.ff(0); int count=1; for(int i=1;i<n;i++) if(q==soul.ff(i)) count++; cout<<count<<endl; } return 0; }
题意:给出每对虫子的互相吸引关系,只有异性才会相互吸引,问虫子当中有没有同性恋==
解决方法:添加一个数组表示每个虫子的性别,注释加讲解
/* 关于并查集,注意两个概念:按秩合并、路径压缩。 1、按秩合并 由于并查集一般是用比较高效的树形结构来表示的,按秩合并的目的就是防止产生退化的树(也就是类似链表的树), 用一个数组记录各个元素的高度(也有的记录各个元素的孩子的数目,具体看哪种能给解题带来方便), 然后在合并的时候把高度小的嫁接到高度大的上面,从而防止产生退化的树。 2、路径压缩 而另一个数组记录各个元素的祖先,这样就防止一步步地递归查找父亲从而损失的时间。因为并查集只要搞清楚各个元素所在的集合, 而区分不同的集合我们用的是代表元素(也就是树根),所以对于每个元素我们只需保存其祖先,从而区分不同的集合。 而我们这道题并没有使用纯正的并查集算法,而是对其进行了扩展, 我们并没有使用“1、按秩合并”(当然你可以用,那样就需要再开一个数组) 我们从“1、按秩合并”得到启示,保存“秩”的数组保存的是 元素相对于父节点的关系 ,我们岂不可以利用这种关系 (即相对于父节点的不同秩值来区分不同的集合),从而可以把两个集合合并成一个集合。 (注:此代码 relation=0 代表 和父节点同一性别) */ #include<stdio.h> int father[2005]; int relation[2005]; int find_father(int i) { int t; if(father[i]==i) return i; //计算相对于新的父节点(即根)的秩,relation[t]是老的父节点相对于新的父节点(即根)的秩,relation[i]是i元素相对于老的父节点的秩, //类似于物理里的相对运动,得到的r[i]就是相对于新的父节点(即根)的秩。而且这个递归调用不会超过两层 t=father[i]; father[i]=find_father(father[i]); relation[i]=(relation[i]+relation[t]+1)%2; //注意递归中把这棵树relation中的的值都更新一遍,这句的顺序 不能 和上一句 调换位置 // relation[a]的改变是伴随着father[a]的改变而更新的(有father改变就有relation改变),要是father改变了,而relation未改变,此时的relation就记录了一个错误的值, //father未改变(即使实际的father已不是现在的值,但只要father未改变,relation的值就是“正确”的,认识到这点很重要。) return father[i]; } void merge(int a,int b) { int x,y; x=find_father(a); y=find_father(b); father[x]=y; relation[x]=(relation[b]-relation[a])%2;//relation[a]+relation[x]与relation[b]相对于新的父节点必须相差1个等级,因为他们不是gay } //x下边的节点不用改,因为查找的时候会自动更新 int main() { int T,m,n,i,j,a,b,flag; scanf("%d",&T); for(i=1;i<=T;++i) { flag=0; scanf("%d%d",&n,&m); for(j=1;j<=n;++j) //初始化 { father[j]=j; relation[j]=1; } for(j=1;j<=m;++j) { scanf("%d%d",&a,&b); if(find_father(a)==find_father(b)) { // if(relation[a]!=(relation[b]+1)%2) if(relation[a]==relation[b]) //说明是同性 flag=1; } else merge(a,b); } if(flag) printf("Scenario #%d:\nSuspicious bugs found!\n\n",i); else printf("Scenario #%d:\nNo suspicious bugs found!\n\n",i); } return 0; }
3、带权并查集经典题:poj1182食物链
我说不明白==参考:http://blog.csdn.net/c0de4fun/article/details/7318642
4、并查集水题 挨着就算相交,给定某个线段,询问这一团的有多少个
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int pre[1010],sum[1010]; struct point{ double x,y; }; struct EDGE{ point a,b; } edge[1010]; int E;//边数 int Find(int x){ return x==pre[x]? x:pre[x]=Find(pre[x]); } void Merge(int a,int b){ int x=Find(a),y=Find(b); if(x!=y){ pre[y]=x; sum[x]+=sum[y]; } } double xmult(point a,point b,point c){//大于零代表a,b,c左转 return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x); } bool OnSegment(point a,point b,point c){ //a,b,c共线时有效 return c.x>=min(a.x,b.x)&&c.x<=max(a.x,b.x)&&c.y>=min(a.y,b.y)&&c.y<=max(a.y,b.y); } bool Cross(point a,point b,point c,point d){//判断ab 与cd是否相交 double d1,d2,d3,d4; d1=xmult(c,d,a); d2=xmult(c,d,b); d3=xmult(a,b,c); d4=xmult(a,b,d); if(d1*d2<0&&d3*d4<0) return 1; else if(d1==0&&OnSegment(c,d,a)) return 1; else if(d2==0&&OnSegment(c,d,b)) return 1; else if(d3==0&&OnSegment(a,b,c)) return 1; else if(d4==0&&OnSegment(a,b,d)) return 1; return 0; } int main() { int i,j,k,T,n; char s[10]; scanf("%d",&T); while(T--) { scanf("%d",&n);E=0; for(i=1;i<=n;i++) pre[i]=i,sum[i]=1; for(i=1;i<=n;i++) { scanf("%s",s); if(s[0]=='P'){ E++; scanf("%lf%lf%lf%lf",&edge[E].a.x,&edge[E].a.y,&edge[E].b.x,&edge[E].b.y); for(j=1;j<E;j++) if(Find(E)!=Find(j)&&Cross(edge[E].a,edge[E].b,edge[j].a,edge[j].b)) Merge(E,j); } else if(s[0]=='Q'){ scanf("%d",&k); printf("%d\n",sum[Find(k)]); } } if(T) printf("\n"); } return 0; }5、并查集浇灌农田:
#include <iostream> #include<cstdio> #include<cstring> using namespace std; bool type[15][4]={{1,0,0,1},{1,1,0,0},{0,0,1,1},{0,1,1,0}, {1,0,1,0},{0,1,0,1},{1,1,0,1},{1,0,1,1},{0,1,1,1},{1,1,1,0},{1,1,1,1}}; int f[300000],n,m; char c; int num[100][100]; void init(int n){ for(int i=1;i<=n;i++) f[i]=i; } int find(int x) { if(x==f[x]) return x; int tmp=f[x]; f[x]=find(f[x]); return f[x]; } int main() { //freopen("cin.txt","r",stdin); while(~scanf("%d%d",&m,&n)) { if(m==-1&&n==-1) break; init(n*m); for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) { cin>>c; num[i][j]=c-65; //cout<<num[i][j]<<" "; } // cout<<endl; } int count=n*m; for(int i=1;i<=m;i++) for(int j=1;j<n;j++) { if(type[num[i][j]][1]&&type[num[i][j+1]][3]) { int fx=find(i*n-n+j),fy=find(i*n-n+j+1); if(fx!=fy) {f[fx]=fy;count--;} } } for(int i=1;i<=n;i++) for(int j=1;j<m;j++) { if(type[num[j][i]][2]&&type[num[j+1][i]][0])//就是这里 这里这里~~ { int fx=find(j*n-n+i),fy=find(j*n+i); if(fx!=fy) {f[fx]=fy;count--;} } } cout<<count<<endl; } return 0; }
题意:问连接两条边是否有环,这个并查集的题用到了find函数的返回值,两个即将连接的点返回值如果相同,那么就说明要成环了!话说是不是好多图论题也可以这么搞呢?
#include <iostream> #include<cstdio> #include<cstring> using namespace std; int a[100005],x,y,count,maxn,edge,point; bool vis[100005],mark; int find(int x) { if(x!=a[x]) a[x]=find(a[x]); return a[x]; } void addto(int x,int y) { x=find(x),y=find(y); if(x==y) { mark=0; return ; } a[y]=a[x]; return ; } void cal() { for(int i=1;i<=maxn;i++) { if(vis[i]) { if(a[i]==i) count++; // point++; if(count>1) mark=0; } } } int main() { // freopen("cin.txt","r",stdin); while(~scanf("%d%d",&x,&y)) { if(x==-1&&y==-1) break; if(x==0&&y==0) { printf("Yes\n"); continue; } memset(vis,0,sizeof(vis)); for(int i=1;i<=100000;i++) a[i]=i; mark=1; count=0; maxn=0; // point=0; addto(x,y); vis[x]=1; vis[y]=1; if(maxn<x) maxn=x; if(maxn<y) maxn=y; while(~scanf("%d%d",&x,&y)) { if(x==0&&y==0) break;/// addto(x,y); vis[x]=1; vis[y]=1; if(maxn<x) maxn=x; if(maxn<y) maxn=y; } cal(); if(mark==1) printf("Yes\n"); else printf("No\n"); } return 0; }
#include <iostream> #include<cstdio> #include<cstring> using namespace std; #define maxn 10000005 #define mod 1000000007 int f[maxn],n,m,l,r,count; bool vis[maxn]; int find(int x) { if(x!=f[x]) f[x]=find(f[x]); return f[x]; } void addto(int x,int y) { x=find(x),y=find(y); if(x==y) return ; f[x]=y; count++; } long long multi(int x) { long long ans=1,tmp=26; while(x) { if(x&1) ans=(ans*tmp)%mod; tmp=(tmp*tmp)%mod; x>>=1; } return ans; } int main() { // freopen("cin.txt","r",stdin); while(~scanf("%d%d",&n,&m)) { for(int i=1;i<=n+1;i++) f[i]=i; count=0; while(m--) { scanf("%d%d",&l,&r); addto(l,r+1); vis[l]=1,vis[r+1]=1; } /*for(int i=1;i<=n+1;i++) { if(vis[i]&&f[i]==i) count++; }*/ printf("%lld\n",multi(n-count)%mod); } return 0; }
#include <iostream> #include <cstdio> #include <cstring> #include <vector> #include <stack> using namespace std; const int N=5006; vector<int>G[N]; struct bridge { int u,v; }bg[2*N]; int vis[N],low[N],dfn[N],Time; int fa[N],deg[N]; int n,m,cnt; void init() { for(int i=0;i<n;i++) G[i].clear(); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(vis,0,sizeof(vis)); memset(deg,0,sizeof(deg)); for(int i=1;i<=n;i++) fa[i]=i; cnt=Time=0; } int findset(int x) { if(x!=fa[x]) fa[x]=findset(fa[x]); return fa[x]; } void Tarjan(int u,int father) { low[u] = dfn[u] = ++Time; vis[u] = 1; for(int i=0;i<G[u].size();i++) { int v = G[u][i]; if(v == father) continue; if(!vis[v]) { Tarjan(v,u); low[u] = min(low[u],low[v]); if(low[v] > dfn[u]) //u->v为桥 bg[cnt].u = u,bg[cnt++].v = v; else //否则,u,v同属一个连通分量,合并 { int fx = findset(u); int fy = findset(v); if(fx != fy) fa[fx] = fy; } } else low[u] = min(low[u],dfn[v]); } } int main() { // freopen("cin.txt","r",stdin); while(~scanf("%d%d", &n, &m)) { init(); for(int i=0;i<m;i++) { int u,v; scanf("%d%d",&u,&v); G[u].push_back(v); G[v].push_back(u); } Tarjan(1,-1); for(int i=0;i<cnt;i++) { int fx=findset(bg[i].u); int fy=findset(bg[i].v); deg[fx]++; deg[fy]++; } int leaf=0; for(int i=1;i<=n;i++) if(deg[i]==1) leaf++; printf("%d\n",(leaf+1)/2); } return 0; }9、
有一个树(<span style=""><span class="katex" style="font-size:1.21em; font-family:KaTeX_Main; line-height:1.2; white-space:nowrap"><span class="katex-mathml" style="position:absolute; padding:0px; border:0px; height:1px; width:1px; overflow:hidden">n</span><span class="katex-html" style="display:inline-block"><span class="strut" style="display:inline-block; height:0.43056em"></span><span class="strut bottom" style="display:inline-block; height:0.43056em; vertical-align:0em"></span><span class="base textstyle uncramped" style="display:inline-block"><span class="mord mathit" style="font-family:KaTeX_Math;font-style:italic">n</span></span></span></span></span>个点, <span style=""><span class="katex" style="font-size:1.21em; font-family:KaTeX_Main; line-height:1.2; white-space:nowrap"><span class="katex-mathml" style="position:absolute; padding:0px; border:0px; height:1px; width:1px; overflow:hidden">n-1</span><span class="katex-html" style="display:inline-block"><span class="strut" style="display:inline-block; height:0.64444em"></span><span class="strut bottom" style="display:inline-block; height:0.72777em; vertical-align:-0.08333em"></span><span class="base textstyle uncramped" style="display:inline-block"><span class="mord mathit" style="font-family:KaTeX_Math;font-style:italic">n</span><span class="mbin" style="margin-left:0.22222em">−</span><span class="mord" style="margin-left:0.22222em">1</span></span></span></span></span>条边的联通图),点标号从<span style=""><span class="katex" style="font-size:1.21em; font-family:KaTeX_Main; line-height:1.2; white-space:nowrap"><span class="katex-mathml" style="position:absolute; padding:0px; border:0px; height:1px; width:1px; overflow:hidden">1</span><span class="katex-html" style="display:inline-block"><span class="strut" style="display:inline-block; height:0.64444em"></span><span class="strut bottom" style="display:inline-block; height:0.64444em; vertical-align:0em"></span><span class="base textstyle uncramped" style="display:inline-block"><span class="mord" style="">1</span></span></span></span></span>~<span style=""><span class="katex" style="font-size:1.21em; font-family:KaTeX_Main; line-height:1.2; white-space:nowrap"><span class="katex-mathml" style="position:absolute; padding:0px; border:0px; height:1px; width:1px; overflow:hidden">n</span><span class="katex-html" style="display:inline-block"><span class="strut" style="display:inline-block; height:0.43056em"></span><span class="strut bottom" style="display:inline-block; height:0.43056em; vertical-align:0em"></span><span class="base textstyle uncramped" style="display:inline-block"><span class="mord mathit" style="font-family:KaTeX_Math;font-style:italic">n</span></span></span></span></span>,树的边权是<span style=""><span class="katex" style="font-size:1.21em; font-family:KaTeX_Main; line-height:1.2; white-space:nowrap"><span class="katex-mathml" style="position:absolute; padding:0px; border:0px; height:1px; width:1px; overflow:hidden">0</span><span class="katex-html" style="display:inline-block"><span class="strut" style="display:inline-block; height:0.64444em"></span><span class="strut bottom" style="display:inline-block; height:0.64444em; vertical-align:0em"></span><span class="base textstyle uncramped" style="display:inline-block"><span class="mord" style="">0</span></span></span></span></span>或<span style=""><span class="katex" style="font-size:1.21em; font-family:KaTeX_Main; line-height:1.2; white-space:nowrap"><span class="katex-mathml" style="position:absolute; padding:0px; border:0px; height:1px; width:1px; overflow:hidden">1</span><span class="katex-html" style="display:inline-block"><span class="strut" style="display:inline-block; height:0.64444em"></span><span class="strut bottom" style="display:inline-block; height:0.64444em; vertical-align:0em"></span><span class="base textstyle uncramped" style="display:inline-block"><span class="mord" style="">1</span></span></span></span></span>.求离每个点最近的点个数(包括自己).距离是0的。两个团连在一起,维护以某点为根节点的点的个数
#include <iostream> #include<cstdio> #include<cstring> using namespace std; int n,t,a[100005],num[100005],cnt; int fnd(int x) { if(x!=a[x]) a[x]=fnd(a[x]); return a[x]; } void addto(int x,int y) { x=fnd(x),y=fnd(y); if(x==y) { return ; } a[y]=a[x]; num[x]+=num[y]; return ; } int main() { // freopen("cin.txt","r",stdin); scanf("%d",&t); while(t--) { scanf("%d",&n); cnt=0; for(int i=1;i<=n;i++) {num[i]=1;a[i]=i;} for(int i=1;i<n;i++) { int u,v,q; scanf("%d%d%d",&u,&v,&q); if(q==0) addto(u,v); } //cout<<"44544"<<endl; cnt=0; for(int i=1;i<=n;i++) { if(a[i]==i&&(num[i]&1)) cnt^=num[i]; } printf("%d\n",cnt); } return 0; }
#include <iostream> #include<cstdio> #include<cstring> #include<vector> using namespace std; int a[100005],deg[100005],odd[100005]; bool used[100005]; int fnd(int x) { if(x!=a[x]) a[x]=fnd(a[x]); return a[x]; } void addto(int x,int y) { x=fnd(x),y=fnd(y); if(x==y) { return ; } a[y]=a[x]; return ; } int main() { // freopen("cin.txt","r",stdin); int n,m,x,y,cnt; while(~scanf("%d%d",&n,&m)) { for(int i=1;i<=n;i++) {a[i]=i;deg[i]=0;used[i]=false;odd[i]=0;} for(int i=0;i<m;i++) { scanf("%d%d",&x,&y); deg[x]++;deg[y]++; x=fnd(x);y=fnd(y); if(x!=y)addto(x,y); } vector<int>v; for(int i=1;i<=n;i++) { int f=fnd(i); if(!used[f]) { v.push_back(f); used[f]=1;//!!! } if(deg[i]&1) odd[f]++; } cnt=0; for(int i=0;i<v.size();i++) { int k=v[i]; if(deg[k]==0) continue; if(odd[k]==0) cnt++; else cnt+=odd[k]/2; } printf("%d\n",cnt); } return 0; }
本来还有两个题的,离线版的lca和左偏堆,