> 文章列表 > [HNOI2014]江南乐

[HNOI2014]江南乐

[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 1000001t100,1n100,1f100000

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

题解

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

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

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

枚举iiimmm,求sgsgsg值,这样做的时间复杂度为O(f2)O(f^2)O(f2),会TLE。

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

⌊im⌋\\lfloor\\dfrac im\\rfloormi相等的一段中,我们来讨论i−i%mi-i\\%mii%mi%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\\rfloorii%m=i(im×mi⌋)=m×mi

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

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

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

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

那么对于每一个含有相同的⌊im⌋\\lfloor\\dfrac im\\rfloormi的段,我们只需要考虑mmmm+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;
}