IOI2021集训队作业 289JE Epic Win

你和另一个人在玩石头剪刀布,双方的出招策略都按照各自的有限状态自动机进行(下一步对方通过这一步我方出什么决定到达哪一个状态)。

现在给出对方的自动机,但是你不知道对方的初始状态。你要造一个自动机使得对于任意对方的初始状态,\(10^9\)场中你的胜率为\(99\%\)

自己的初始状态是你自己钦定的。

\(n\le 100\)

自己的自动机的状态数不超过\(50000\)


好玩的模型。

首先胜率如此之高,基本可以确定:构造出一个方案使得失败的次数为有限次。

大概方针是:通过有限次步数来确定对方在哪个状态,后面照着对方的自动机虐爆对方。

设对方可能的状态集合为\(S\),在当前步中,你要决定出什么,对方同时出,下一局开始的时候你就知道对方在这一局出了什么。下一局开始时根据对方这一局出什么对\(S\)进行分裂,分成三个集合继续进行确认。

现在我们的目的是让\(S\)尽可能分裂。

预处理\(f_{x,y}\)表示,如果要分辨\(x,y\),那么这一步需要选什么。转移:\(f_{to_{x,c},to_{y,c}}\to f_{x,y}\)。这个转移是有后效性的,所以通过宽搜的方式转移。这样就可以\(O(n^2)\)的时间复杂度预处理。

根据类似UNR#4 同构判定鸭的结论得,似乎只需要转移\(2n\)(还是\(n\)都过了)层(至今不会证明)。所以只需要\(O(n)\)的时间就可以区分两个状态。

于是在当前局面中,如果\(S\)中存在不同构,那么按照\(f\)的指示来走;如果同构就顺着对方的自动机完胜。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 105
#define M 50005
int n;
int s[N],to[N][3];
int f[N][N];
int q[N];
int cnt,as[M],ato[M][3];
void dfs(int[],int,int);
void divide(int q[],int k,int id){
	static int q_[3][N];
	int k_[3];
	memset(k_,0,sizeof k_);
	for (int i=0;i<k;++i)
		q_[s[q[i]]][k_[s[q[i]]]++]=to[q[i]][as[id]];
	memcpy(q,q_[0],sizeof(int)*k_[0]);
	memcpy(q+k_[0],q_[1],sizeof(int)*k_[1]);
	memcpy(q+k_[0]+k_[1],q_[2],sizeof(int)*k_[2]);
	if (k_[0]) 
		dfs(q,k_[0],ato[id][0]=++cnt); 
	else ato[id][0]=1;
	if (k_[1]) 
		dfs(q+k_[0],k_[1],ato[id][1]=++cnt); 
	else ato[id][1]=1;
	if (k_[2]) 
		dfs(q+k_[0]+k_[1],k_[2],ato[id][2]=++cnt); 
	else ato[id][2]=1;
}
int vis[N],BZ,p[N];
void build(int x,int id){
//	printf("x=%d id=%d\n",x,id);
	vis[x]=BZ,p[x]=id;
	as[id]=(s[x]+1)%3;
	x=to[x][as[id]];
	if (vis[x]==BZ)
		ato[id][0]=ato[id][1]=ato[id][2]=p[x];
	else{
		ato[id][0]=ato[id][1]=ato[id][2]=++cnt;
		build(x,cnt);
	}
}
void dfs(int q[],int k,int id){
//	printf("%d %d %d\n",k,q[0],q0[1]);
	for (int i=1;i<k;++i)
		if (f[q[0]][q[i]]!=-1){
			int c=f[q[0]][q[i]];
			as[id]=c;
			divide(q,k,id);
			return;
		}
	++BZ;
	build(q[0],id);
}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	scanf("%d",&n);
	char str[2];
	for (int i=1;i<=n;++i){
		scanf("%s%d%d%d",str,&to[i][0],&to[i][1],&to[i][2]);
		s[i]=(*str=='R'?0:*str=='P'?1:2);
	}
	for (int i=1;i<=n;++i)
		for (int j=1;j<=n;++j)
			if (s[i]==s[j])
				f[i][j]=-1;
	for (int k=1;k<=n;++k)
		for (int i=1;i<=n;++i)
			for (int j=1;j<=n;++j)
				if (f[i][j]==-1){
					for (int c=0;c<3;++c)
						if (f[to[i][c]][to[j][c]]!=-1){
							f[i][j]=c;
							break;
						}
				}
	for (int i=0;i<n;++i)
		q[i]=i+1;
	dfs(q,n,++cnt);
	printf("%d\n",cnt);
	for (int i=1;i<=cnt;++i)
		printf("%c %d %d %d\n",as[i]==0?'R':as[i]==1?'P':'S',ato[i][0],ato[i][1],ato[i][2]);
	return 0;
}
posted @ 2020-10-23 11:38  jz_597  阅读(167)  评论(2编辑  收藏  举报