@TryMyEdge
2017-02-08T16:22:35.000000Z
字数 6448
阅读 1175
题解
A 确定比赛名次
题目大意:
有N(1<=N<=500)只队伍,M个关系,第i个关系包括两个整数ai和bi(1<=ai,bi<=N),表示ai队的排名在bi队前面。要求按从前到后的顺序输出榜单,不能确定关系的编号小的队在前。题目保证可以输出一个合法的榜单。
多组数据。
解题思路:
裸的拓扑排序。
因为要求编号小的队伍在前,所以改用优先队列跑拓扑。
时间复杂度o(n*log(n)+m),空间复杂度o(n+m)。
AC代码:
#include<iostream>#include<functional>#include<cstdio>#include<cmath>#include<queue>#include<algorithm>#include<cstring>#define pq priority_queue#define Pi acos(-1.0)using namespace std;#define MOD 1000000007queue <int> q[505];int deg[505];pq <int,vector<int>,greater<int> > p;int main(){int n,m,a,b;while(~scanf("%d%d",&n,&m)){while(m--){scanf("%d%d",&a,&b);deg[b]++;q[a].push(b);}for(int i=1;i<=n;i++){if(deg[i]==0)p.push(i);}m=0;while(!p.empty()){a=p.top();p.pop();if(m)printf(" ");elsem=1;printf("%d",a);while(!q[a].empty()){b=q[a].front();q[a].pop();deg[b]--;if(deg[b]==0)p.push(b);}}printf("\n");}return 0;}
B Reward
题目大意:
有n(n<=10000)个工人,m(m<=20000)个关系,第i个关系包括两个整数ai和bi(1<=ai,bi<=n),表示ai号工人的奖金要比在bi号工人高。每个人的奖金最少为888元。求一共最少发多少奖金,如果没有合法的方案输出-1。
多组数据。
解题思路:
预处理最开始在队列里的那些工人的奖金为888,跑拓扑的时候顺便更新一下每个点的最少工资。记录有多少个点进过队列,如果不足n个则说明有环,没有合法方案。
时间复杂度o(m+n),空间复杂度o(m+n)。
AC代码:
#include<iostream>#include<cstdio>#include<cmath>#include<queue>#include<algorithm>#include<cstring>#define pq priority_queue#define Pi acos(-1.0)using namespace std;#define MOD 1000000007queue <int> q[10005],p;int deg[10005],cost[10005];int main(){int n,m,a,b;int ans;while(~scanf("%d%d",&n,&m)){ans=0;for(int i=1;i<=n;i++){while(!q[i].empty()) q[i].pop();deg[i]=0;}while(m--){scanf("%d%d",&a,&b);deg[a]++;q[b].push(a);}memset(cost,0,sizeof(cost));for(int i=1;i<=n;i++){if(deg[i]==0){p.push(i);cost[i]=888;}}m=0;while(!p.empty()){a=p.front();p.pop();ans+=cost[a];m++;while(!q[a].empty()){b=q[a].front();q[a].pop();deg[b]--;cost[b]=max(cost[b],cost[a]+1);if(deg[b]==0)p.push(b);}}if(n>m)printf("-1\n");elseprintf("%d\n",ans);}return 0;}
C Rank of Tetris
题目大意:
有N(0<=N<=10000)个人,M(0<=M<=20000)个关系,第i个关系说明了两个人的rating关系为前者高于后者、两者一样或后者高于前者。两个rating一样的人编号大的排前面。求能不能根据这些信息确定一张榜单,如果不能是因为信息冲突还是因为信息不足。两种原因都有的情况下判断为信息冲突。
多组数据。
解题思路:
因为rating一样的人的排名顺序是确定的而且是连续的,所以先用并查集把所有相等关系用并查集处理合并,再用剩下的并查集的根节点跑拓扑。如果某个时刻队列里同时存在超过一个点,则这两个点的顺序是不确定的,所以信息不足。如果有环则说明信息冲突。根据几个标志按题目要求的优先度输出即可。
小细节:注意处理完合并点数可能已经发生变化,判断信息冲突的时候应该按合并后的点数算。用来跑拓扑的边应该是连在两端点所属并查集的根节点上。
时间复杂度o(m+n),空间复杂度o(n+m)。
AC代码:
#include<iostream>#include<cstdio>#include<cmath>#include<queue>#include<algorithm>#include<cstring>#define pq priority_queue#define Pi acos(-1.0)using namespace std;#define MOD 1000000007queue <int> q[10005],p;int deg[10005];int root[10005];int findr(int x){if(root[x]==x)return x;elsereturn root[x]=findr(root[x]);}struct cp{int x,y;}temp;queue <cp> mq;int main(){int n,nt,m,a,b,now;char s[6];int flag;while(~scanf("%d%d",&n,&m)){nt=n;flag=0;for(int i=0;i<n;i++){root[i]=i;while(!q[i].empty()) q[i].pop();deg[i]=0;}while(m--){scanf("%d%s%d",&a,s,&b);if(s[0]=='='){a=findr(a);b=findr(b);if(a!=b){root[a]=b;n--;}}else{if(s[0]=='>'){temp.x=a;temp.y=b;}else{temp.x=b;temp.y=a;}mq.push(temp);}}m=0;while(!mq.empty()){temp=mq.front();mq.pop();temp.x=findr(temp.x);temp.y=findr(temp.y);deg[temp.x]++;q[temp.y].push(temp.x);}for(int i=0;i<nt;i++){if(deg[i]==0 && findr(i)==i)p.push(i);}while(!p.empty()){if(p.size()>1)flag=1;a=p.front();p.pop();m++;while(!q[a].empty()){b=q[a].front();q[a].pop();deg[b]--;if(deg[b]==0)p.push(b);}}if(n>m)printf("CONFLICT\n");else{if(flag)printf("UNCERTAIN\n");elseprintf("OK\n");}}return 0;}
D 欧拉回路
题目大意:
给定一个N(1<N<=1000)个点、M条边的无向图。问是否存在欧拉回路。
多组数据。
解题思路:
欧拉回路结论题。一个无向图有欧拉回路的充要条件:(1)图连通(2)每个点的度都是偶数。
所以在输入的同时维护每个节点的度,并且用并查集维护连通性即可。
时间复杂度o(n+m),空间复杂度o(n)。
AC代码:
#include<iostream>#include<cstdio>#include<cmath>#include<queue>#include<algorithm>#include<cstring>#define pq priority_queue#define Pi acos(-1.0)using namespace std;#define MOD 1000000007int deg[1005];int root[1005];int findr(int x){if(root[x]==x)return x;elsereturn root[x]=findr(root[x]);}int main(){int n,m,a,b;int flag;while(scanf("%d",&n),n){scanf("%d",&m);flag=1;for(int i=1;i<=n;i++){root[i]=i;deg[i]=0;}while(m--){scanf("%d%d",&a,&b);deg[a]++;deg[b]++;a=findr(a);b=findr(b);if(a>b)root[a]=b;elseroot[b]=a;}for(int i=1;i<=n;i++){if(root[i]!=1 || deg[i]%2)flag=0;}printf("%d\n",flag);}return 0;}
E Play on Words
题目大意:
有N(1<=N<=100000)个单词,问是否存在这N个单词的某种排列,满足每个单词的第一个字母和前一个单词的最后一个字母相同。
T组数据。
解题思路:
把26个字母看成26个点,每个单词看成起点为首字母终点为尾字母的有向边,题目就转化为判断有向图是否有欧拉路径。
一个有向图有欧拉路的充要条件:(1)图半连通(2)出度=入度+1的点为潜在起点,入度=出度+1的点为潜在终点,潜在终点和潜在起点都为0或都为1且其余点的出度都等于入度。
所以在输入的时候维护一下每条边起点出度和终点的入度,同时合并这两个点所属的并查集,最终按照以上两个条件判断即可。
小细节:26个字母可能只出现了一部分,注意判断。
时间复杂度o(N),空间复杂度o(1000)。
AC代码:
#include<iostream>#include<cstdio>#include<cmath>#include<queue>#include<algorithm>#include<cstring>#define pq priority_queue#define Pi acos(-1.0)using namespace std;#define MOD 1000000007int deg1[666],deg2[666];int root[666];bool life[666];char s[1005];int findr(int x){if(root[x]==x)return x;elsereturn root[x]=findr(root[x]);}int main(){int t,l,m,a,b,gg1,gg2,gg;int flag;cin>>t;while(t--){scanf("%d",&m);flag=0;for(int i='a';i<='z';i++){root[i]=i;deg1[i]=deg2[i]=0;life[i]=0;}while(m--){scanf("%s",s);l=strlen(s);a=s[0];b=s[l-1];deg1[a]++;deg2[b]++;life[a]=1;life[b]=1;a=findr(a);b=findr(b);root[a]=b;gg=b;}gg1=gg2=0;for(int i='a';i<='z';i++){if(deg1[i]!=deg2[i]){if(deg1[i]==deg2[i]+1)gg1++;else{if(deg1[i]==deg2[i]-1)gg2++;elseflag=1;}}if(findr(i)!=gg && life[i])flag=1;}if(gg1>1 || gg2>1 || gg1!=gg2)flag=1;if(flag)printf("The door cannot be opened.\n");elseprintf("Ordering is possible.\n");}return 0;}
F The Best Path
题目大意:
有N(N<=100000)个湖,第i个湖的幸运值为ai(0<=ai<=10000),M(M<=500000)条河,每条河连接两个湖,河是双向的并且有可能连接的湖是同一个。幸运值为一路上经过的所有的湖的幸运值的异或和。问能不能从一个湖出发,经过所有的河,如果能的话求出最大的幸运值。
解题思路:
判断能不能经过所有的河就是判断图有没有欧拉路。如果存在,并且不是欧拉回路,那么每个点的访问次数都是固定的,为度数的一半,起点和终点各多访问一次。一个数如果异或奇数次那么异或和的值为它本身,如果异或偶数次那么异或和的值为0,并且异或满足交换律。由此可以计算出幸运值。如果是欧拉回路,那么每个点被访问次数为度数的一半,起点和终点为同一个点,会被多访问一次。于是求出剩余点的幸运值之后,枚举起点取最大的幸运值。
时间复杂度o(n+m),空间复杂度o(n+m)。
AC代码:
#include<iostream>#include<cstdio>#include<cmath>#include<queue>#include<algorithm>#include<cstring>#define pq priority_queue#define Pi acos(-1.0)using namespace std;#define MOD 1000000007int deg[100005];int root[100005];int num[100005];int findr(int x){if(root[x]==x)return x;elsereturn root[x]=findr(root[x]);}int main(){int t,n,m,a,b,gg1,gg2,gg;int flag,ans1,ans2;cin>>t;while(t--){scanf("%d%d",&n,&m);flag=0;ans1=0;ans2=0;for(int i=1;i<=n;i++){root[i]=i;deg[i]=0;scanf("%d",num+i);}while(m--){scanf("%d%d",&a,&b);deg[a]++;deg[b]++;a=findr(a);b=findr(b);root[a]=b;gg=b;}m=0;for(int i=1;i<=n;i++){if((deg[i]/2)%2)ans1^=num[i];if(findr(i)!=gg)flag=1;if(deg[i]%2){m++;ans2^=num[i];}}if(m!=0 && m!=2)flag=1;if(flag)printf("Impossible\n");else{if(m==2)ans2^=ans1;else{for(int i=1;i<=n;i++)ans2=max(ans2,ans1^num[i]);}printf("%d\n",ans2);}}return 0;}