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+5 typedef 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; }
题解:带权并查集,推出公式 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+5 typedef 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+5 typedef 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+5 typedef 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+5 typedef 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+5 typedef 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; }