[HNOI2014]江南乐

P3235 [HNOI2014]江南乐

题目大意

t t t组游戏和一个数 f f f,每组游戏有 n n n堆石子,两个人轮流操作。

每次操作,玩家可以选定一个不小于 2 2 2的正整数 m m m,然后将任意一堆石子数量大于等于 f f f的石子分成 m m m堆,使这 m m m堆中石子数最多的一堆至多比石子数最少的一堆多至少一堆石子。(即尽量平均分配)。当一个玩家不能操作的时候,他就输了。

两个人都采用最优策略。如果先手必胜,则输出 1 1 1;否则输出 0 0 0

数据范围

1 ≤ t ≤ 100 , 1 ≤ n ≤ 100 , 1 ≤ f ≤ 100000 1\leq t\leq 100,1\leq n\leq 100,1\leq f\leq 100000 1t100,1n100,1f100000

每堆石子的数量 ≤ 100000 \leq 100000 100000


题解

首先,我们要求在只有一堆石子的时候的 s g sg sg值。初始值为 s g i = 0 ( 1 ≤ i < f ) sg_i=0(1\leq isgi=0(1i<f)

若当前的这堆石子有 i i i个,枚举其分成 m m m堆,那么石子个数为 ⌊ i m ⌋ \lfloor\dfrac im\rfloor mi的有 i − i % m i-i\%m ii%m堆石子,石子个数为 ⌊ i m ⌋ + 1 \lfloor\dfrac im\rfloor+1 mi+1的有 i % m i\%m i%m堆石子。我们只需要求它们的异或和即可。

我们可以发现,如果 i − i % m i-i\%m ii%m为偶数,我们不需要异或 ⌊ i m ⌋ \lfloor\dfrac im\rfloor mi,只有 i − i % m i-i\%m ii%m为奇数时才要异或。 i % m i\%m i%m ⌊ i m ⌋ + 1 \lfloor\dfrac im\rfloor+1 mi+1也同理。那么,在已知 i i i m m m的情况下,可以 O ( 1 ) O(1) O(1)求出其异或和。

枚举 i i i m m m,求 s g sg sg值,这样做的时间复杂度为 O ( f 2 ) O(f^2) O(f2),会TLE。

我们考虑优化。上文得出石子个数为 ⌊ i m ⌋ \lfloor\dfrac im\rfloor mi的有 i − i % m i-i\%m ii%m堆石子,石子个数为 ⌊ i m ⌋ + 1 \lfloor\dfrac im\rfloor+1 mi+1的有 i % m i\%m i%m堆石子。我们可以发现,在 m m m取一段值的时候, ⌊ i m ⌋ \lfloor\dfrac im\rfloor mi是不变的(这就是数论分块)。

⌊ i m ⌋ \lfloor\dfrac im\rfloor mi相等的一段中,我们来讨论 i − i % m i-i\%m ii%m i % m i\%m i%m的奇偶性。

一类石子: i − i % m = i − ( i − m × ⌊ i m ⌋ ) = m × ⌊ i m ⌋ i-i\%m=i-(i-m\times \lfloor\dfrac im\rfloor)=m\times \lfloor\dfrac im\rfloor ii%m=i(im×mi⌋)=m×mi

二类石子: i % m = i − m × ⌊ i m ⌋ i\%m=i-m\times \lfloor\dfrac im\rfloor i%m=im×mi

m = m + 1 m=m+1 m=m+1时,一类石子的数量增加了 ⌊ i m ⌋ \lfloor\dfrac im\rfloor mi,二类石子的数量减少了 ⌊ i m ⌋ \lfloor\dfrac im\rfloor mi

  • 如果 ⌊ i m ⌋ \lfloor\dfrac im\rfloor mi为奇数,则两类石子的数量的奇偶性都改变
  • 如果 ⌊ i m ⌋ \lfloor\dfrac im\rfloor mi为偶数,则两类石子的数量的奇偶性都不变

m = m + 2 m=m+2 m=m+2时两类石子的奇偶性一定和原来的奇偶性相同,所以不用考虑, m + 3 m+3 m+3的和 m + 1 m+1 m+1时也相同,以此类推。

那么对于每一个含有相同的 ⌊ i m ⌋ \lfloor\dfrac im\rfloor mi的段,我们只需要考虑 m m m m + 1 m+1 m+1的情况。

时间复杂度为 O ( f f ) O(f\sqrt f) O(ff )

code

#include
#include
using namespace std;
int t,f,n,ans,a[105],z[100005],sg[100005];
int main()
{
	scanf("%d%d",&t,&f);
	for(int i=1;i<f;i++){
		sg[i]=0;
	}
	for(int i=f;i<=100000;i++){
		for(int j=2;j<=i;j=i/(i/j)+1){
			for(int k=j;k<=min(i,j+1);k++){
				int v1=i/k,v2=i%k,now=0;
				if(v2&1) now^=sg[v1+1];
				if(k-v2&1) now^=sg[v1];
				z[now]=i;
			}
		}
		int x=0;
		for(;z[x]==i;x++);
		sg[i]=x;
	}
	while(t--){
		scanf("%d",&n);
		ans=0;
		for(int i=1,x;i<=n;i++){
			scanf("%d",&x);
			ans^=sg[x];
		}
		if(ans) printf("1 ");
		else printf("0 ");
	}
	return 0;
}

你可能感兴趣的:(题解,题解)