1. 程式人生 > >【NOI2015模擬YDC】遊戲

【NOI2015模擬YDC】遊戲

Description

有一個有n個格子的東西,其中有一些格子中有棋子。每一次先手可以選擇一個棋子移到它右邊第一個沒有棋子的位置。先佔領格子n的玩家獲勝。求先手必勝的方案數。
n<=10^9,棋子數<=10^6

Solution

首先,讓我們來看一看一次操作的本質。
它相當於把一段連續的棋子向右移動了一格。
然後,我們要把這個遊戲轉化為我們熟悉的遊戲。
“對於一段連續的棋子,我們可以把任意數量的從右開始的連續的棋子向右移動一格”
階梯nim!
很明顯,如果n-2有棋子,那麼都不會有人動它。
那麼,我們從n-2開始,是第0層。然後遇到一段連續的棋子,就把這一層加上這一段的棋子數,並且跳過這一段左邊的那個空白。
如果只遇到空白,就把層數+1.
這樣,我們就可以用樸素做法了。
但是這道題並不是判輸贏,而是求方案數?
沒事,移動之後sg=0就好了。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 1000005
using namespace std;
int a[N],b[N],c[N],d[N],n,m,t,sg,ans;
int main() {
    scanf("%d%d",&m,&n);
    fo(i,1,n) scanf("%d",&a[i]);
    if (a[n]==m-1
) { for(int i=n;a[i-1]+1==a[i];i--) t=i-1; printf("%d",n-t+1);return 0; } a[n+1]=m-1;a[0]=-1; for(int i=n;i;) { int j=i;t+=a[i+1]-a[i]-1; while (a[j-1]+1==a[j]) j--; if (t%2) b[++b[0]]=i-j+1; else if (t) { c[++c[0]]=i-j+1; if
(a[i]+2==a[i+1]) d[c[0]]=b[b[0]]; } i=j-1; } fo(i,1,b[0]) sg^=b[i]; if (!sg) {printf("0");return 0;} fo(i,1,b[0]) if ((b[i]^sg)<=b[i]) ans++; fo(i,1,c[0]) if ((d[i]^sg)<=d[i]+c[i]&&(d[i]^sg)>d[i]) ans++; printf("%d",ans); }