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 1≤t≤100,1≤n≤100,1≤f≤100000
每堆石子的数量 ≤ 100000 \leq 100000 ≤100000
首先,我们要求在只有一堆石子的时候的 s g sg sg值。初始值为 s g i = 0 ( 1 ≤ i < f ) sg_i=0(1\leq i
若当前的这堆石子有 i i i个,枚举其分成 m m m堆,那么石子个数为 ⌊ i m ⌋ \lfloor\dfrac im\rfloor ⌊mi⌋的有 i − i % m i-i\%m i−i%m堆石子,石子个数为 ⌊ i m ⌋ + 1 \lfloor\dfrac im\rfloor+1 ⌊mi⌋+1的有 i % m i\%m i%m堆石子。我们只需要求它们的异或和即可。
我们可以发现,如果 i − i % m i-i\%m i−i%m为偶数,我们不需要异或 ⌊ i m ⌋ \lfloor\dfrac im\rfloor ⌊mi⌋,只有 i − i % m i-i\%m i−i%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 i−i%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 i−i%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 i−i%m=i−(i−m×⌊mi⌋)=m×⌊mi⌋
二类石子: i % m = i − m × ⌊ i m ⌋ i\%m=i-m\times \lfloor\dfrac im\rfloor i%m=i−m×⌊mi⌋
当 m = m + 1 m=m+1 m=m+1时,一类石子的数量增加了 ⌊ 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)。
#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;
}