[URAL1540]Battle for the Ring

I.[URAL1540]Battle for the Ring

这大约是我做的第一道SG函数的题(

很容易想到一个区间DP状态:设 \(f_{i,j,k}\) 表示第 \(i\) 条链子,\([j,k]\) 这一段的SG值。

于是我们枚举这一段中删掉了小于等于某个值的元素进行转移。如果删掉的值形成了多个串,依照SG函数的性质,取值应是其异或。

但是这题因为需要保证复杂度在 \(O(kn^3)\) 以内,所以大概不能太随性地转移。我采取的方法是,因为每一个 \(f_{i,j,k}\) 都是一个集合 \(g_{i,j,k}\) 中的 \(\text{mex}\),所以我们考虑由 \(g_{i,j,k-1}\) 推出 \(g_{i,j,k}\),这是可以在 \(O(n)\) 之内解决的。于是复杂度就是严格 \(O(kn^3)\),但是有一大坨细节。如果允许带个 \(\log\),那就可以非常简单地用类珂朵莉树的结构维护,但是我没敢试(

代码:

#include<bits/stdc++.h>
using namespace std;
int n,res;
const int LIM=100;
struct Chain{
	int a[110],f[110][110],len,g[110][110],las[110][110],mn[110][110];
	bool occ[110];
	void read(){scanf("%d",&len);for(int i=1;i<=len;i++)scanf("%d",&a[i]);}
	void DP(){
		memset(mn,0x3f,sizeof(mn));
		for(int i=len;i;i--)for(int j=i;j<=len;j++){
			mn[i][j]=min(mn[i][j-1],a[j]);
			for(int k=1;k<=LIM;k++){
				if(a[j]<=k){g[j][k]=g[j-1][k];continue;}
				if(i==j||a[j-1]<=k){las[j][k]=j,g[j][k]=g[j-1][k]^1;continue;}
				g[j][k]=g[j-1][k]^f[las[j-1][k]][j-1]^f[las[j-1][k]][j],las[j][k]=las[j-1][k];
			}
//			printf("(%d %d)\n",i,j);
			for(int k=1;k<=LIM;k++)occ[k]=false;
			for(int k=mn[i][j];k<=LIM;k++)occ[g[j][k]]=true;
			for(int k=0;k<=LIM;k++)if(!occ[k]){f[i][j]=k;break;}
			for(int k=1;k<mn[i][j];k++)g[j][k]=f[i][j];
//			printf("%d\n",f[i][j]);
		}
	}
	int ALL(){return f[1][len];}
	int CHECK(){
		res^=f[1][len];
//		printf("%d\n",res);
		for(int i=1;i<=len;i++){
			int tot=0;
			for(int j=1,k=1;j<=len;j=k){
				if(a[j]<=a[i]){k++;continue;}
				k=j;while(k<=len&&a[k]>a[i])k++;
				tot^=f[j][k-1];
			}
//			printf("%d:%d\n",i,tot);
			if(tot==res)return i;
		}
		res^=f[1][len];
		return -1;
	}
}c[60];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)c[i].read(),c[i].DP(),res^=c[i].ALL();
	if(!res){puts("S");return 0;}
	puts("G");
	for(int i=1;i<=n;i++){
		int tmp=c[i].CHECK();
		if(tmp==-1)continue;
		printf("%d %d\n",i,tmp);
		return 0;
	}
	return 0;
}
posted @ 2021-04-01 12:29  Troverld  阅读(32)  评论(0编辑  收藏  举报