kuangbin带你飞 专题五 并查集
来源:互联网 发布:flash编程语言 编辑:程序博客网 时间:2024/05/16 12:55
POJ 2236
题意:给你n个点的坐标,然后修理几个点,然后问两点之间是否连同(连同的条件是边权小于d)
题解:先edge存两点之间的边权,然后每次维修一个点之后,把所有与他相连的点中已经维修并且边权小于d的点放到一个并查集中,即可,数据有点大,一开始以为会T,结果很水。
hdu 3038
题解:带权并查集,左端点-1之后用带权并查集做:
#include <iostream>#include <cstdio>#include <cctype>#include <cstdlib>#include <cmath>#include <algorithm>#include <cstring>#include <string>#include <vector>#include <queue>#include <map>#include <set>#include <sstream>#include <stack>using namespace std;#define MAX 200000+5#define MAXN 100000+5typedef long long LL;typedef unsigned long long ull;const double pi=3.141592653589793;const int INF=0x3f3f3f3f;const int INFF=1e9;const double inf=1e18;const double eps=1e-10;const int mod=1000000007;const int prime=999983;inline int read_int(){ int ret=0; char tmp; while(!isdigit(tmp=getchar())); do{ ret=(ret<<3)+(ret<<1)+tmp-'0'; }while(isdigit(tmp=getchar())); return ret;}int pre[MAX];int tmp[MAX];//在父节点的右边多少int Find(int x){ if(pre[x]==x) return x; int temp=pre[x]; pre[x]=Find(pre[x]); tmp[x]=tmp[x]+tmp[temp]; return pre[x];}void Union(int a,int b,int aa,int bb,int c){ pre[bb]=aa; tmp[bb]=tmp[a]+c-tmp[b];}int main(){ int n,m; while(~scanf("%d%d",&n,&m)){ for(int i=0;i<=n;i++){ pre[i]=i; tmp[i]=0; } int ans=0; for(int i=0;i<m;i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); a--; int aa=Find(a); int bb=Find(b); if(aa!=bb) Union(a,b,aa,bb,c); else{ if(tmp[a]+c!=tmp[b]) ans++; } } printf("%d\n",ans); } return 0;}
poj 1182
题解:带权并查集,推出公式 0同类1被父亲吃,2吃父亲
#include <iostream>#include <cstdio>#include <cctype>#include <cstdlib>#include <cmath>#include <algorithm>#include <cstring>#include <string>#include <vector>#include <queue>#include <map>#include <set>#include <sstream>#include <stack>using namespace std;#define MAX 50000+5#define MAXN 100000+5typedef long long LL;typedef unsigned long long ull;const double pi=3.141592653589793;const int INF=0x3f3f3f3f;const int INFF=1e9;const double inf=1e18;const double eps=1e-10;const int mod=1000000007;const int prime=999983;inline int read_int(){ int ret=0; char tmp; while(!isdigit(tmp=getchar())); do{ ret=(ret<<3)+(ret<<1)+tmp-'0'; }while(isdigit(tmp=getchar())); return ret;}int pre[MAX];int tmp[MAX];//0和父亲同类,1被父亲吃,2吃父亲int Find(int x){ if(pre[x]==x) return x; int temp=pre[x]; pre[x]=Find(pre[x]); tmp[x]=(tmp[x]+tmp[temp])%3; return pre[x];}void Union(int a,int b,int aa,int bb,int c){ pre[bb]=aa; tmp[bb]=(tmp[a]+c-1+3-tmp[b])%3;}int main(){ int n,k; scanf("%d%d",&n,&k); for(int i=1;i<=n;i++){ pre[i]=i; tmp[i]=0; } int ans=0; for(int i=0;i<k;i++){ int d,x,y; scanf("%d%d%d",&d,&x,&y); if(x>n||y>n){ ans++; continue; } if(d==2&&x==y){ ans++; continue; } int xx=Find(x); int yy=Find(y); if(xx!=yy) Union(x,y,xx,yy,d); else{ if((tmp[x]+d+3-tmp[y])%3!=1) ans++; } } printf("%d\n",ans); return 0;}poj 1456
题意:给你n个物品,有价值和最后期限,每天卖一个,问你最多可以赚多少钱
题解:按价值排序,然后如果最后期限那天被占了,就往前移动,找没有被占的那天,贪心。。。
放在并查集专题里,估计是找到一个卖出日期之后就去找他前面还没有被占的日期,然后连起来。。。。
while(~scanf("%d",&n)){ memset(f,0,sizeof(f)); for(int i=0;i<n;i++){ scanf("%d%d",&good[i].p,&good[i].d); } sort(good,good+n,cmp); int sum=0; for(int i=0;i<n;i++){ int dd=good[i].d; for(int j=dd;j>0;j--){ if(!f[j]){ f[j]=1; sum+=good[i].p; break; } } } printf("%d\n",sum); }
poj 1733
题意:给你一个很长的序列,然后告诉你一些区间,每个区间中有奇数还是偶数个1,然后从第一句开始判断,一旦有错误就输出有多少个。
题解:序列长10E,非常大,显然要离散化,直接用map就行,不过一开始输入的区间左端点要-1,然后就是用带权并查集计算。(一开始WA了好多次,都想不明白,原来是我只在出现错误语句时输出,如果所有语句都是正确的我就没有输出,哎还是太傻了。。。。)
#include <iostream>#include <cstdio>#include <cctype>#include <cstdlib>#include <cmath>#include <algorithm>#include <cstring>#include <string>#include <vector>#include <queue>#include <map>#include <set>#include <sstream>#include <stack>using namespace std;#define MAX 10000+5#define MAXN 100000+5typedef long long LL;typedef unsigned long long ull;const double pi=3.141592653589793;const int INF=0x3f3f3f3f;const int INFF=1e9;const double inf=1e18;const double eps=1e-10;const int mod=1000000007;const int prime=999983;inline int read_int(){ int ret=0; char tmp; while(!isdigit(tmp=getchar())); do{ ret=(ret<<3)+(ret<<1)+tmp-'0'; }while(isdigit(tmp=getchar())); return ret;}struct question{ int x,y; int is_odd;}p[5005];int pre[10005];int tmp[10005];int Find(int x){ if(pre[x]==x) return x; int temp=pre[x]; pre[x]=Find(pre[x]); tmp[x]=(tmp[x]+tmp[temp])%2; return pre[x];}void Union(int a,int b,int aa,int bb,int c){ pre[bb]=aa; tmp[bb]=(tmp[a]+c+2-tmp[b])%2;}int main(){ int n,m; scanf("%d%d",&n,&m); map<int,int> ma; int xcount=0; for(int i=0;i<m;i++){ int a,b; string s; cin>>a>>b>>s; a--; if(!ma.count(a)) ma[a]=xcount++; if(!ma.count(b)) ma[b]=xcount++; if(s=="odd") p[i]=(question){ma[a],ma[b],1}; else p[i]=(question){ma[a],ma[b],0}; } for(int i=0;i<xcount;i++){ pre[i]=i; tmp[i]=0; } int flag =0; for(int i=0;i<m;i++){ int a=p[i].x; int b=p[i].y; int aa=Find(a); int bb=Find(b); if(aa!=bb) Union(a,b,aa,bb,p[i].is_odd); else{ if(tmp[a]!=(p[i].is_odd+tmp[b])%2){ printf("%d\n",i); return 0; } } } printf("%d\n",m); return 0;}
poj 1984
题意:给你很多路,每个路都有长度和方向,然后给你k次询问,问你造了几条路的时候这两个点是否连同,如果连同输出路的长度,不连同输出-1
题解:这题40000的点和路,10000次询问,数据很大,而且给的询问不一定按照顺序,所以需要离线输出,而且有东南西北方向,可以化成x,y轴的距离,带权并查集需要用结构体(也可以不用)(哎T了好久,最后发现离线存了之后排序,可以直接一遍按照顺序把边扫完,结果我忘记每次更新开始的边的位置了TAT是手搓啊)
#include <iostream>#include <cstdio>#include <cctype>#include <cstdlib>#include <cmath>#include <algorithm>#include <cstring>#include <string>#include <vector>#include <queue>#include <map>#include <set>#include <sstream>#include <stack>using namespace std;#define MAX 40000+5#define MAXN 100000+5typedef long long LL;typedef unsigned long long ull;const double pi=3.141592653589793;const int INF=0x3f3f3f3f;const int INFF=1e9;const double inf=1e18;const double eps=1e-10;const int mod=1000000007;const int prime=999983;inline int read_int(){ int ret=0; char tmp; while(!isdigit(tmp=getchar())); do{ ret=(ret<<3)+(ret<<1)+tmp-'0'; }while(isdigit(tmp=getchar())); return ret;}struct Distance{ int x,y;}dis[MAX];struct Edge{ int u,v,costx,costy;}edge[MAX];struct query{ int a,b,c,id;}q[10005];int pre[MAX];int ans[10005];char dir[4]={'W','E','S','N'};int dx[4]={-1,1,0,0};int dy[4]={0,0,1,-1};bool cmp1(query aa,query bb){ return aa.c<bb.c;}int Find(int t){ if(pre[t]==t) return t; int temp=pre[t]; pre[t]=Find(pre[t]); dis[t]=(Distance){dis[t].x+dis[temp].x,dis[t].y+dis[temp].y}; return pre[t];}void Union(int a,int b,int aa,int bb,int x,int y){ pre[bb]=aa; dis[bb]=(Distance){dis[a].x-dis[b].x+x,dis[a].y-dis[b].y+y};}int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=0;i<m;i++){ int a,b,c,e; char d; scanf("%d %d %d %c",&a,&b,&c,&d); for(int j=0;j<4;j++){ if(d==dir[j]&&j<2){ edge[i]=(Edge){a,b,dx[j]*c,0}; } else if(d==dir[j]&&j>=2){ edge[i]=(Edge){a,b,0,dy[j]*c}; } } } int k; scanf("%d",&k); for(int i=0;i<k;i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); q[i]=(query){a,b,c,i}; } sort(q,q+k,cmp1); for(int i=1;i<=n;i++){ pre[i]=i; dis[i]=(Distance){0,0}; } int before=0; for(int i=0;i<k;i++){ int a=q[i].a; int b=q[i].b; for(int j=before;j<q[i].c;j++){ int x=edge[j].u; int y=edge[j].v; int xx=Find(x); int yy=Find(y); if(xx!=yy) Union(x,y,xx,yy,edge[j].costx,edge[j].costy); } if(Find(a)!=Find(b)) ans[q[i].id]=-1; else ans[q[i].id]=abs(dis[a].x-dis[b].x)+abs(dis[a].y-dis[b].y); before=q[i].c; } for(int i=0;i<k;i++){ printf("%d\n",ans[i]); } return 0;}
poj 2912
题意:给你n个人玩石头剪刀布,里面有一个是裁判,其余人任意分成三组,然后给你很多胜负关系,裁判可以随便乱出,然后找出谁是裁判。
题解:枚举1-n谁是裁判,如果当前是第k个人,如果他是裁判,下面的胜负关系有矛盾,记录下出错的行数,说明他不是裁判。
然后遍历1-n,看有多少个人当裁判的时候出错了,如果只有一个人没出错,那么他肯定是裁判,判断他的行数就是记录的出错的行数中的最大值(因为每个出错行数可以排除一个人是裁判,所以n-1个出错行数的最大值就能排除n-1个人)如果没出错的大于1,就不能判断,如果等于0,就是不可能。
#include <iostream>#include <cstdio>#include <cctype>#include <cstdlib>#include <cmath>#include <algorithm>#include <cstring>#include <string>#include <vector>#include <queue>#include <map>#include <set>#include <sstream>#include <stack>using namespace std;#define MAX 10000+5#define MAXN 100000+5typedef long long LL;typedef unsigned long long ull;const double pi=3.141592653589793;const int INF=0x3f3f3f3f;const int INFF=1e9;const double inf=1e18;const double eps=1e-10;const int mod=1000000007;const int prime=999983;inline int read_int(){ int ret=0; char tmp; while(!isdigit(tmp=getchar())); do{ ret=(ret<<3)+(ret<<1)+tmp-'0'; }while(isdigit(tmp=getchar())); return ret;}int pre[505];int tmp[505];//0相等,1比父亲大,2比父亲小int a[MAX];int b[MAX];int d[MAX];int error[MAX];int Find(int x){ if(pre[x]==x) return x; int temp=pre[x]; pre[x]=Find(pre[x]); tmp[x]=(tmp[x]+tmp[temp])%3; return pre[x];}void Union(int a,int b,int aa,int bb,int d){ pre[bb]=aa; tmp[bb]=(tmp[a]+d+3-tmp[b])%3;}int main(){ int n,m; while(~scanf("%d%d",&n,&m)){ memset(error,-1,sizeof(error)); for(int i=0;i<m;i++){ char c; scanf("%d%c%d",&a[i],&c,&b[i]); if(c=='=') d[i]=0; if(c=='<') d[i]=1; if(c=='>') d[i]=2; } for(int k=0;k<n;k++){ for(int i=0;i<n;i++){ pre[i]=i; tmp[i]=0; } for(int i=0;i<m;i++){ if(a[i]==k||b[i]==k) continue; int aa=Find(a[i]); int bb=Find(b[i]); if(aa!=bb) Union(a[i],b[i],aa,bb,d[i]); else { if((tmp[a[i]]+d[i])%3!=tmp[b[i]]){ error[k]=i+1; break; } } } } int ans=0; int cnt=0; int line=0; for(int i=0;i<n;i++){ if(error[i]==-1){ ans++; cnt=i; } line=max(line,error[i]); } if(ans==0) printf("Impossible\n"); else if(ans>1) printf("Can not determine\n"); else printf("Player %d can be determined to be the judge after %d lines\n",cnt,line); } return 0;}ZOJ 3261
题意:给你n个星球它们都有各自的能量,然后m条路连接这些星球,然后q次询问,query是问a星球周围连接的星球能量最大的星球的编号(并且这个能量要大于a的能量),如果编号相同,就输出编号最小的,destroy就是破坏两个星球间的那条路。
题解:这题就是个神坑,首先是每两个输出之间要一个空行,然后是询问次数比较多,可以离线,可以先把所有边存进set中,然后把要破坏的边给去掉(假装都破坏完了),然后倒着遍历询问,碰到query就把答案放进vector中,碰到destroy就把破坏的边连上,就OK了,不过我segmentation fault了半天,后来才发现是询问数组开小了5W啊我当成1W了,然后又WA,后来发现是存边的时候结构体里重载<重载反了(话说我只是扫一遍set,重载反了有问题么?)
#include <iostream>#include <cstdio>#include <cctype>#include <cstdlib>#include <cmath>#include <algorithm>#include <cstring>#include <string>#include <vector>#include <queue>#include <map>#include <set>#include <sstream>#include <stack>#pragma comment(linider, "/STACid:1024000000,1024000000")using namespace std;#define MAX 10000+5#define MAXN 100000+5typedef long long LL;typedef unsigned long long ull;const double pi=3.141592653589793;const int INF=0x3f3f3f3f;const int INFF=1e9;const double inf=1e18;const double eps=1e-10;const int mod=1000000007;const int prime=999983;inline int read_int(){ int ret=0; char tmp; while(!isdigit(tmp=getchar())); do{ ret=(ret<<3)+(ret<<1)+tmp-'0'; }while(isdigit(tmp=getchar())); return ret;}struct Edge{ int u,v; bool operator < (const Edge &a)const{ if(u==a.u) return v<a.v; return u<a.u; }};struct query{ int a,b,id;}que[5*MAX];int pre[MAX];int power[MAX];int val[MAX];set<Edge> s;int Find(int x){ if(pre[x]==x) return x; int temp=pre[x]; pre[x]=Find(pre[x]); val[x]=max(val[x],val[temp]); return pre[x];}void Union(int a,int b){ int aa=Find(a); int bb=Find(b); if(aa==bb) return; if((val[aa]>val[bb])||(val[aa]==val[bb]&&aa<bb)) pre[bb]=aa; else pre[aa]=bb;}int main(){ int n,m; int flag=0; while(~scanf("%d",&n)){ if(!flag) flag=1; else printf("\n"); s.clear(); for(int i=0;i<n;i++){ scanf("%d",&power[i]); val[i]=power[i]; pre[i]=i; } scanf("%d",&m); for(int i=0;i<m;i++){ int a,b; scanf("%d%d",&a,&b); if(a>b) swap(a,b); Edge e=(Edge){a,b}; s.insert(e); } int q; scanf("%d",&q); for(int i=0;i<q;i++){ string c; cin>>c; if(c[0]=='q'){ int a; scanf("%d",&a); que[i].id=0; que[i].a=a; } else{ int a,b; scanf("%d%d",&a,&b); que[i].id=1; if(a>b) swap(a,b); que[i].a=a; que[i].b=b; Edge e=(Edge){a,b}; s.erase(e); } } for(set<Edge>::iterator it=s.begin();it!=s.end();it++){ Edge k=*it; Union(k.u,k.v); } vector<int> ans; ans.clear(); for(int i=q-1;i>=0;i--){ if(que[i].id==0){ int aa=Find(que[i].a); if(val[aa]>power[que[i].a]) ans.push_back(aa); else ans.push_back(-1); } else{ Union(que[i].a,que[i].b); } } for(int i=ans.size()-1;i>=0;i--){ printf("%d\n",ans[i]); } } return 0;}
- kuangbin带你飞 专题五 并查集
- [kuangbin带你飞]专题五 【并查集】 【--完结--】
- 【 题集 】 【kuangbin带你飞】专题五 并查集 更新ing...
- POJ 2236 A - Wireless Network[kuangbin带你飞]专题五 并查集
- hdu 1213 How Many Tables ([kuangbin带你飞]专题五 并查集)
- hdu 1272 小希的迷宫[kuangbin带你飞]专题五 并查集
- [kuangbin带你飞]专题五 并查集 A POJ 2236
- [kuangbin带你飞]专题五 并查集 B POJ 1611
- [kuangbin带你飞]专题五 并查集 C HDU 1213
- [kuangbin带你飞]专题五 并查集 D HDU 3038
- [kuangbin带你飞]专题五 并查集 E POJ 1182
- [kuangbin带你飞]专题五 并查集 M HDU 1272
- [kuangbin带你飞]专题五 并查集 F POJ 1417
- [kuangbin带你飞]专题五 并查集 G POJ 1456
- [kuangbin带你飞]专题五 并查集 H POJ 1733
- [kuangbin带你飞]专题五 并查集 J POJ 2492
- [kuangbin带你飞]专题五 并查集 N POJ 1308
- [kuangbin带你飞]专题五 并查集 I POJ 1984
- 阿里云 RDS SDK python3支持
- java深入解析-第2章 运算符与表达式
- 查看某个php函数的简单方法
- 霓歌即时通讯中的相关专利整理(一)
- [华为机试练习题]49.向升序单向链表中插入一个节点
- kuangbin带你飞 专题五 并查集
- ExMobi文档
- php中or的使用
- Drools入门-----------环境搭建,分析Helloworld
- linux解压文件
- Hibernate笔记总结整理
- dataguard中的gap及修复方式(增量备份方式/手工传送注册方式)
- SD卡热插拔
- 黑马程序员---C语言基础---Switch语句