@TryMyEdge
2017-02-09T00:22:35.000000Z
字数 6448
阅读 1047
题解
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 1000000007
queue <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(" ");
else
m=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 1000000007
queue <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");
else
printf("%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 1000000007
queue <int> q[10005],p;
int deg[10005];
int root[10005];
int findr(int x)
{
if(root[x]==x)
return x;
else
return 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");
else
printf("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 1000000007
int deg[1005];
int root[1005];
int findr(int x)
{
if(root[x]==x)
return x;
else
return 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;
else
root[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 1000000007
int deg1[666],deg2[666];
int root[666];
bool life[666];
char s[1005];
int findr(int x)
{
if(root[x]==x)
return x;
else
return 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++;
else
flag=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");
else
printf("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 1000000007
int deg[100005];
int root[100005];
int num[100005];
int findr(int x)
{
if(root[x]==x)
return x;
else
return 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;
}