[NOIp复习计划]:并查集
2017-07-17 00:31
253 查看
[bzoj 1370] [Baltic2003]Gang团伙
把一个点x拆成两个点x1,x2,与x1合并就代表是朋友,与x2合并就表示是敌人,这样处理的好处就是,假设y,z是x的敌人,那么把x2,与y,z分别合并时,y与x也就间接合并了,最后统计答案就好了。
[bzoj 1529][POI2005]ska Piggy banks
把钥匙的包含关系看成边,那么就形成了好多联通块,每个联通块中只要敲掉一个,每个就都可以访问到,维护联通块用并查集啦。
[bzoj 1116][POI2008]CLO
好吧先补一下基础知识,一个节点的入度和出度是在有向图中定义的,无向图并没有这种说法,于是树形结构是题意不满足的。
根据上面的定义我们可以知道,只要有个环就可以满足题意,可以脑补一下,对于每个极小环,我们可以按顺序构造有向边,顺时针或逆时针,当然环上每个点不一定只有一条边,对于那些边,我们可以选择把它设为方向向外指的有向边去连接一个树形结构,或是直接保留无向边(不对入度有贡献),去连接另一个环。
当然图也有不联通的现象。
这样题目就转化成了判断每个联通块中是否有环。
判断的方法,是在每个集合的根处维护一个bool值,代表该集合是否有环。
例如:合并x,y时,如果x,y在同一集合就把根的值设为true,不再一个集合,就把y并向x,根的值为x根的值|y根的值。
[bzoj 1015][JSOI2008]星球大战starwar
在线的方法不好想。感觉按秩合并并查集可做
考虑离线做法,一开就把所有路炸掉,然后倒着做。
这就变成了,每次合并两个集合,并维护联通块的个数,就可以随便搞了。
今年1月写的代码风格怎么跟现在差那么多
[bzoj 1854][Scoi2010]游戏
是个裸的二分图模型
我们把属性看成点,把装备看成边,这样对于每个联通块,假设有n个点,如果有环,那么n个都可以用,而如果没有环,那就只能有n-1个。
下面是令人称奇的微操。并没有
vis[i]表示i能不能用
设当前要合并两个x,y,x<y
如果它们不属于一个集合,那么比较x的根和y的根的大小,把小的并向大的,然后把小的根的vis设为true。
这是因为我们要维护每个集合的根是该集合内元素的极大值,这样方便更新vis的值。
而如果它们在一个集合里,那么就把根的vis设为true就好了,这是因为这个联通块里有环,所以最大值就可以取了。
最后从1-100000看看vis,更新答案就好了。
[bzoj 1202][HNOI2005]狡猾的商人
把每个月看成点,因为要维护值,考虑带权并查集,每个节点多维护一个值表示与父节点值的差值。
然后乱搞就行了233.
to be continued……
把一个点x拆成两个点x1,x2,与x1合并就代表是朋友,与x2合并就表示是敌人,这样处理的好处就是,假设y,z是x的敌人,那么把x2,与y,z分别合并时,y与x也就间接合并了,最后统计答案就好了。
#include<cstdio> #include<algorithm> using namespace std; int fa[100010],a[100010]; int n,m,x,y,ans; char ch[3]; inline int getfa(int x) {return fa[x]==x?x:fa[x]=getfa(fa[x]);} int main() { scanf("%d%d",&n,&m); for(int i=1;i<=2*n;i++)fa[i]=i; for (int i=1;i<=m;i++) { scanf("%s%d%d",ch+1,&x,&y); if (ch[1]=='F')fa[getfa(x)]=getfa(y); else { fa[getfa(x)]=getfa(n+y); fa[getfa(y)]=getfa(n+x); } } for(int i=1;i<=n;i++)a[i]=getfa(i); sort(a+1,a+n+1); ans=1; for(int i=2;i<=n;i++)if(a[i]!=a[i-1])ans++; printf("%d\n",ans); return 0; }
[bzoj 1529][POI2005]ska Piggy banks
把钥匙的包含关系看成边,那么就形成了好多联通块,每个联通块中只要敲掉一个,每个就都可以访问到,维护联通块用并查集啦。
#include<bits/stdc++.h> #define N 1000005 using namespace std; int n,ans; int fa ; int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} int main() { scanf("%d",&n); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=n;i++){ int x;scanf("%d",&x); int p=find(i),q=find(x); if(p!=q)fa[q]=i; } for(int i=1;i<=n;i++) if(fa[i]==i)ans++; printf("%d",ans); return 0; }
[bzoj 1116][POI2008]CLO
好吧先补一下基础知识,一个节点的入度和出度是在有向图中定义的,无向图并没有这种说法,于是树形结构是题意不满足的。
根据上面的定义我们可以知道,只要有个环就可以满足题意,可以脑补一下,对于每个极小环,我们可以按顺序构造有向边,顺时针或逆时针,当然环上每个点不一定只有一条边,对于那些边,我们可以选择把它设为方向向外指的有向边去连接一个树形结构,或是直接保留无向边(不对入度有贡献),去连接另一个环。
当然图也有不联通的现象。
这样题目就转化成了判断每个联通块中是否有环。
判断的方法,是在每个集合的根处维护一个bool值,代表该集合是否有环。
例如:合并x,y时,如果x,y在同一集合就把根的值设为true,不再一个集合,就把y并向x,根的值为x根的值|y根的值。
#include<cstdio> const int MAXN = 100010; int fa[MAXN]; bool tag[MAXN]; int getroot(int x){ return fa[x]==x?x:fa[x]=getroot(fa[x]); } void unite(int x,int y){ int f_x=getroot(x); int f_y=getroot(y); if(f_x==f_y){ tag[f_x]=1; }else{ fa[f_x]=f_y; tag[f_y]|=tag[f_x]; } } int main(){ int n,m; scanf("%d %d",&n,&m); for(int i=1;i<=n;i++)fa[i]=i; for(int i=1;i<=m;i++){ int a,b; scanf("%d %d",&a,&b); unite(a,b); } for(int i=1;i<=n;i++){ if(fa[i]==i && tag[i]==0){ puts("NIE"); return 0; } } puts("TAK"); return 0; }
[bzoj 1015][JSOI2008]星球大战starwar
在线的方法不好想。感觉按秩合并并查集可做
考虑离线做法,一开就把所有路炸掉,然后倒着做。
这就变成了,每次合并两个集合,并维护联通块的个数,就可以随便搞了。
今年1月写的代码风格怎么跟现在差那么多
#include<cstdio> #define rep(i,a,b) for(i=a;i<=b;i++) #define dep(i,a,b) for(i=a;i>=b;i--) #define maxn 200010 int fa[maxn<<1],h[maxn<<1],nx[maxn<<2],to[maxn<<2],tm[maxn<<1],ans[maxn<<1],mk[maxn<<1],len,m,n,cnt,cntOfTm; int getroot(int x){ return fa[x]==x?x:fa[x]=getroot(fa[x]); } void Find(int &ret,int nd){ ret=getroot(nd); } #define Merge(aaa,bbb) {int ra,rb;Find(ra,aaa);Find(rb,bbb);if(ra!=rb)cnt--;fa[rb]=ra;} #define Init(){int ii;rep(ii,1,n)fa[ii]=ii;} #define AddEdge(a,b){len++;nx[len]=h[a];to[len]=b;h[a]=len;} int main() { int i,j,k,a,b; scanf("%d%d",&n,&m); Init(); rep(i,1,m) { scanf("%d%d",&a,&b);a++;b++; AddEdge(a,b);AddEdge(b,a); } scanf("%d",&cntOfTm); rep(i,1,cntOfTm){ scanf("%d",&a);a++;tm[a]=1;mk[i]=a; } rep(i,1,n) { if(tm[i]!=0) continue; cnt++; for(j=h[i];j;j=nx[j]) { if(tm[to[j]]==0) { Merge(i,to[j]); } } } ans[cntOfTm]=cnt; dep(i,cntOfTm,1) { tm[mk[i]]=0;cnt++; for(j=h[mk[i]];j;j=nx[j]) { if(tm[to[j]]==0) { Merge(to[j],mk[i]); } } ans[i-1]=cnt; } rep(i,0,cntOfTm) printf("%d\n",ans[i]); return 0; }
[bzoj 1854][Scoi2010]游戏
是个裸的二分图模型
我们把属性看成点,把装备看成边,这样对于每个联通块,假设有n个点,如果有环,那么n个都可以用,而如果没有环,那就只能有n-1个。
下面是令人称奇的微操。并没有
vis[i]表示i能不能用
设当前要合并两个x,y,x<y
如果它们不属于一个集合,那么比较x的根和y的根的大小,把小的并向大的,然后把小的根的vis设为true。
这是因为我们要维护每个集合的根是该集合内元素的极大值,这样方便更新vis的值。
而如果它们在一个集合里,那么就把根的vis设为true就好了,这是因为这个联通块里有环,所以最大值就可以取了。
最后从1-100000看看vis,更新答案就好了。
#include<cstdio> #define rep(i,a,b) for(i=a;i<=b;i++) #define dep(i,a,b) for(i=a;i>=b;i--) const int MAXN = 1000005; int n,fa[MAXN]; bool right[MAXN]; int GetRoot(int x){return fa[x]==x?x:fa[x]=GetRoot(fa[x]);} void solve(int x,int y){ int Fx=GetRoot(x),Fy=GetRoot(y); if(Fx==Fy){right[Fy]|=1;} else{if(Fx>Fy){int t=Fx;Fx=Fy;Fy=t;}fa[Fx]=Fy;right[Fx]|=1;} } int main(){ int i,a,b; scanf("%d",&n); rep(i,1,100006)fa[i]=i; rep(i,1,n){ scanf("%d%d",&a,&b); solve(a,b); } rep(i,1,100005){ if(!right[i]){ printf("%d",i-1); return 0; } } return 0; }
[bzoj 1202][HNOI2005]狡猾的商人
把每个月看成点,因为要维护值,考虑带权并查集,每个节点多维护一个值表示与父节点值的差值。
然后乱搞就行了233.
#include<cstdio> #define rep(i,a,b) for(i=a;i<=b;i++) #define dep(i,a,b) for(i=a;i>=b;i--) #define MAXN 120 int fa[MAXN],val[MAXN],t,n,m; void ReSet(){int i;rep(i,0,MAXN-1)fa[i]=i,val[i]=0;} int GetRoot(int x){if(fa[x]==x)return fa[x];int k=GetRoot(fa[x]);val[x]+=val[fa[x]];fa[x]=k;return k;} int main(){ int i,a,b,c; bool flag=0; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); ReSet(); flag=0; rep(i,1,m){ scanf("%d%d%d",&a,&b,&c);a--; int Fa=GetRoot(a),Fb=GetRoot(b); if(Fa==Fb){ if(val[b]-val[a]!=c && flag==0){ puts("false"); flag=1; } }else{ fa[Fa]=Fb; val[Fa]=val[b]-val[a]-c; } } if(!flag)puts("true"); } return 0; }
to be continued……
相关文章推荐
- [NOIp复习计划]:构造
- [NOIp复习计划]:模拟
- [置顶] NOIP 2017 复习计划
- NOIP复习计划
- [NOIp复习计划]:贪心
- [NOIp复习计划]:图的连通
- [NOIp复习计划]:差分约束
- NOIP复习计划
- [NOIp复习计划]:二分答案
- [ 冲刺NOIP2016 ] 复习计划
- 暑期复习计划。
- NOIP2015 运输计划 解题报告(二分答案+树状差分)
- POJ 1182 食物链 (并查集)(需复习!!)
- 【NOIP2017练习】论战大原题(并查集)
- [bzoj4326]NOIP2015 运输计划
- NOIP2015 T6 运输计划 【树上差分】
- [置顶] noip冲刺计划(no regrets,no fear)
- JZOJ 3804. 【NOIP2014模拟8.24】小X 的AK 计划
- NOIP2015 运输计划
- 【反演复习计划】【bzoj3994】DZY loves maths