题解:P10686 Rochambeau
思路
根据题面,由于裁判可以随便出招,所以若 \(p\) 是作弊者裁判,所以关于 \(p\) 的所有对局都没有意义,无法得到任何有效信息。那么,如果确定 \(p\) 为裁判,则关于 \(p\) 的所有对局都可以忽略。
因此,再根据 \(\sout{N \le 500}\) 的数据,可以考虑枚举裁判 \(p\)。根据题意,没有裁判的时候所有对局都应当是合法的,所以在忽略与 \(p\) 相关的所有对局以后,若还能发现不合法的对局,则在此时可证明 \(p\) 不是裁判。
枚举所有的 \(p\) 以后,若合法的裁判数量为 \(0\),即没有合法的裁判,不可能完成所有对局,输出 Impossible
。
如果合法的裁判数量大于 \(1\),此时无法确定裁判到底是谁,输出 Can not determine
。
如果合法的裁判数量刚好为 \(1\),此时有且仅有一个人可能是裁判,输出 Player x can be determined to be the judge after y lines
,\(x\) 为这个合法的裁判,\(y\) 为确定这个合法裁判所需的最小游戏轮数,其求法会在接下来讲到。
如何确定唯一的合法裁判?很明显,当你排除掉其他所有人选以后,这个唯一合法裁判自然就确定了。所以,确定唯一合法裁判所需最小游戏轮数就是证明最后一个人不是裁判的时间。
算法
首先读入所有数据并储存。
枚举每一个 \(p \in [1,n]\)。对于每一个 \(p\),依次扫描所有对局,若对局双方有 \(p\),则直接跳过 (作弊者打的比赛不算数)。
对于每一个对局,如果为 a>b
,那么 swap(a,b)
,把它转为 a<b
。这样,对局就只剩下 a=b
和 a<b
。
本题中小朋友被分为 \(A, B, C\) 三部分,其中 \(A<B, B<C, C<A\),和 P2024 食物链 中的“\(A\) 吃 \(B\),\(B\) 吃 \(C\),\(C\) 吃 \(A\)”很像,所以可以开一个三倍大小的种类并查集来维护三部分之间的关系。
a=b
和 a<b
可以类比 P2024 食物链 中的“\(x,y\) 时同类”和“\(x\) 吃 \(y\)”两种情况,用种类并查集的敌对和朋友关系维护并判断矛盾。
一旦发现矛盾,证明 \(p\) 不是裁判,记录发生矛盾的时间。
如果扫完所有对局以后还没有发现矛盾,\(p\) 就是一个合法的裁判,累加裁判数量并记录 \(p\) 以备输出所用。
枚举完所有的 \(p\) 以后,
- 如果裁判数量等于 \(0\),输出
Impossible
- 如果裁判数量等于 \(1\),输出
Player x can be determined to be the judge after y lines
,其中 \(x\) 为刚才记录的 \(p\),\(y\) 为所有发生矛盾时间的最大值 - 如果裁判数量大于 \(1\),输出
Can not determine
AC 代码(附注释)
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=505,M=2005;
int n,m;
struct Peter{
int p1,p2;
int res; //0表示p1=p2; 1表示p1<p2; 3表示p1>p2;
}game[M];
int ufa[N*3]; //并查集模板,注意开空间和初始化时是三倍
inline void uInit()
{
for(int i=1;i<=n*3;i++)
ufa[i]=i;
return;
}
int uget(const int x)
{
if(ufa[x]==x) return x;
else return ufa[x]=uget(ufa[x]);
}
inline void umerge(const int x,const int y)
{
ufa[uget(y)]=uget(x);
return;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=1;i<=m;i++) //记录所有的对局
{
int x,y; char op;
scanf("%d%c%d",&x,&op,&y);
if(op=='=') game[i]={x,y,0};
if(op=='<') game[i]={x,y,1};
if(op=='>') game[i]={x,y,2};
}
uInit();
int possible_judge=0,judge=0,last=0;
for(int p=1;p<=n;p++)
{
uInit();
int contract=0x3f3f3f3f;
for(int i=1;i<=m;i++)
{
int x=game[i].p1+1,y=game[i].p2+1,res=game[i].res;
if(x==p||y==p) continue; //忽略带有 p 的对局
if(res==2) swap(x,y); //x>y 转 x<y
if(res==0) //x=y
{
if(uget(x)==uget(y+n)||uget(x+n)==uget(y)) contract=min(contract,i); //记录矛盾发生时间,下同
else if(uget(x+n)==uget(y+n+n)||uget(x+n+n)==uget(y+n)) contract=min(contract,i);
else if(uget(x+n+n)==uget(y)||uget(x)==uget(y+n+n)) contract=min(contract,i);
else umerge(x,y),umerge(x+n,y+n),umerge(x+n+n,y+n+n);
}
else //x<y
{
if(uget(x)==uget(y)||uget(x+n)==uget(y+n)||uget(x+n+n)==uget(y+n+n)) contract=min(contract,i);
else if(uget(x+n)==uget(y)||uget(x+n+n)==uget(y+n)||uget(x)==uget(y+n+n)) contract=min(contract,i);
else umerge(x,y+n),umerge(x+n,y+n+n),umerge(x+n+n,y);
}
}
if(contract==0x3f3f3f3f) possible_judge++,judge=p; //未发现矛盾
else last=max(last,contract); //发现矛盾
}
if(possible_judge==0) printf("Impossible\n");
else if(possible_judge==1) printf("Player %d can be determined to be the judge after %d lines\n",judge-1,last);
else printf("Can not determine\n");
}
}
2024-08-07 20:47 撰写于洛谷,2025-08-29 20:45 迁移至博客园。
本文采用 「CC-BY-NC 4.0」 创作共享协议,转载请注明作者及出处,禁止商业使用。
作者:Jerrycyx,原文链接:https://www.cnblogs.com/jerrycyx/p/19065150