LG P3235 [HNOI2014]江南乐(SG函数)

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/88584848

题目
首先这个题SG函数很明显。
枚举分为多少堆然后对子状态的SG函数求mex就行。
但是直接枚举是 O ( n 2 ) O(n^2)
可以用整除分块:
n个石子分成m堆:
n n % m m 个大小为 n m + 1 \lfloor\frac nm\rfloor+1 的石堆。
m n m-n % m m 个大小为 n m \lfloor\frac nm\rfloor 的石堆。
现在只有 r = n r=n % m m m r m-r 的大小不确定了。
发现如果 u = n m u=\lfloor\frac nm\rfloor
m + + m++ r = u r-=u , m r > m r + u + 1 m-r->m-r+u+1
所以u为奇数时m-r奇偶不变,r奇偶变。
u为偶数时反之。

AC Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100005
using namespace std;

int T,F,n;
int SG[maxn];
int vis[maxn],tim;

int ser(int x){
	if(SG[x]>=0) return SG[x];
	for(int i=2,nxt;i<=x;i=nxt+1){
		nxt=x/(x/i);
		int u = x / i;
		ser(u),ser(min(u+1,x-1));
	}
	++tim;
	for(int i=2,nxt;i<=x;i=nxt+1){
		nxt=x/(x/i);
		int u = x / i , r = x % i , SG1 = ser(u) , SG2 = ser(min(u+1,x-1));
		vis[(((i-r)&1)*SG1)^((r&1)*SG2)] = tim;
		if(i<nxt && r>=u) r-=u,i++,vis[(((i-r)&1)*SG1)^((r&1)*SG2)] = tim;
	}
	for(SG[x]=0;vis[SG[x]]==tim;SG[x]++);
	return SG[x];
}

int main(){
	memset(SG,-1,sizeof SG);
	scanf("%d%d",&T,&F);
	for(int i=0;i<F;i++) SG[i]=0;
	SG[1]=0;
	for(;T--;){
		scanf("%d",&n);
		int ans = 0;
		for(int i=1,x;i<=n;i++){
			scanf("%d",&x);
			ans ^= ser(x);
		}	
		if(ans == 0) printf("%d%c",0,T==0?'\n':' ');
		else printf("%d%c",1,T==0?'\n':' ');
	}
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/88584848