[HNOI2014]江南乐
P3235 [HNOI2014]江南乐
题目大意
有ttt组游戏和一个数fff,每组游戏有nnn堆石子,两个人轮流操作。
每次操作,玩家可以选定一个不小于222的正整数mmm,然后将任意一堆石子数量大于等于fff的石子分成mmm堆,使这mmm堆中石子数最多的一堆至多比石子数最少的一堆多至少一堆石子。(即尽量平均分配)。当一个玩家不能操作的时候,他就输了。
两个人都采用最优策略。如果先手必胜,则输出111;否则输出000。
数据范围
1≤t≤100,1≤n≤100,1≤f≤1000001\\leq t\\leq 100,1\\leq n\\leq 100,1\\leq f\\leq 1000001≤t≤100,1≤n≤100,1≤f≤100000
每堆石子的数量≤100000\\leq 100000≤100000
题解
首先,我们要求在只有一堆石子的时候的sgsgsg值。初始值为sgi=0(1≤i<f)sg_i=0(1\\leq i<f)sgi=0(1≤i<f)。
若当前的这堆石子有iii个,枚举其分成mmm堆,那么石子个数为⌊im⌋\\lfloor\\dfrac im\\rfloor⌊mi⌋的有i−i%mi-i\\%mi−i%m堆石子,石子个数为⌊im⌋+1\\lfloor\\dfrac im\\rfloor+1⌊mi⌋+1的有i%mi\\%mi%m堆石子。我们只需要求它们的异或和即可。
我们可以发现,如果i−i%mi-i\\%mi−i%m为偶数,我们不需要异或⌊im⌋\\lfloor\\dfrac im\\rfloor⌊mi⌋,只有i−i%mi-i\\%mi−i%m为奇数时才要异或。i%mi\\%mi%m和⌊im⌋+1\\lfloor\\dfrac im\\rfloor+1⌊mi⌋+1也同理。那么,在已知iii和mmm的情况下,可以O(1)O(1)O(1)求出其异或和。
枚举iii和mmm,求sgsgsg值,这样做的时间复杂度为O(f2)O(f^2)O(f2),会TLE。
我们考虑优化。上文得出石子个数为⌊im⌋\\lfloor\\dfrac im\\rfloor⌊mi⌋的有i−i%mi-i\\%mi−i%m堆石子,石子个数为⌊im⌋+1\\lfloor\\dfrac im\\rfloor+1⌊mi⌋+1的有i%mi\\%mi%m堆石子。我们可以发现,在mmm取一段值的时候,⌊im⌋\\lfloor\\dfrac im\\rfloor⌊mi⌋是不变的(这就是数论分块)。
在⌊im⌋\\lfloor\\dfrac im\\rfloor⌊mi⌋相等的一段中,我们来讨论i−i%mi-i\\%mi−i%m和i%mi\\%mi%m的奇偶性。
一类石子:i−i%m=i−(i−m×⌊im⌋)=m×⌊im⌋i-i\\%m=i-(i-m\\times \\lfloor\\dfrac im\\rfloor)=m\\times \\lfloor\\dfrac im\\rfloori−i%m=i−(i−m×⌊mi⌋)=m×⌊mi⌋
二类石子:i%m=i−m×⌊im⌋i\\%m=i-m\\times \\lfloor\\dfrac im\\rfloori%m=i−m×⌊mi⌋
当m=m+1m=m+1m=m+1时,一类石子的数量增加了⌊im⌋\\lfloor\\dfrac im\\rfloor⌊mi⌋,二类石子的数量减少了⌊im⌋\\lfloor\\dfrac im\\rfloor⌊mi⌋
- 如果⌊im⌋\\lfloor\\dfrac im\\rfloor⌊mi⌋为奇数,则两类石子的数量的奇偶性都改变
- 如果⌊im⌋\\lfloor\\dfrac im\\rfloor⌊mi⌋为偶数,则两类石子的数量的奇偶性都不变
在m=m+2m=m+2m=m+2时两类石子的奇偶性一定和原来的奇偶性相同,所以不用考虑,m+3m+3m+3的和m+1m+1m+1时也相同,以此类推。
那么对于每一个含有相同的⌊im⌋\\lfloor\\dfrac im\\rfloor⌊mi⌋的段,我们只需要考虑mmm和m+1m+1m+1的情况。
时间复杂度为O(ff)O(f\\sqrt f)O(ff)。
code
#include<iostream>
#include<cstdio>
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;
}