F. Attack on Red Kingdom(SG博弈 game 週期)

http://codeforces.com/problemset/problem/1312/F

題意:

給出n個數,有3種攻擊方式,分別是選擇一個正數減去x,y,z
n3e5,ai1e18,x,y,z5n\leq 3e5,a_i\leq 1e18,x,y,z\leq 5)。

對於一個數,第2種和第三種攻擊不能對其連續實施。(上次使用第二種攻擊的這個數,下次攻擊這個數就不能用第二種)

兩個人的game。誰攻擊完後所有數變爲0誰獲勝。問先手有多少第一步的下法,使得自己必勝。(不同數或者不同攻擊算不同的走法)

解析:

不能連續使用的限制固定在每個數自身上,所以可以進行單獨遊戲SG計算再異或。

考慮單個遊戲的SG計算。

對於狀態數特別大的SG計算,有兩種想法:打表找規律,找循環節。這裏顯然是後者。因爲變化不多,只有3種,所以SG值[0,4]\in[0,4]。並且每次i[i5,i1]i\to [i-5,i-1]

單個狀態無非是SG[i][j]表示值爲i上次攻擊爲j。然後將SG[i4,i][0,2]SG[i-4,i][0,2]這15個狀態塞進map,找到循環節。

最後,對每個數嘗試每種攻擊之後的狀態,判斷是不是0(對對面必輸態)。

代碼:

/*
 *  Author : Jk_Chen
 *    Date : 2020-03-25-13.55.54
 */
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair<int, int>
#define fi first
#define se second
void test(){cerr<<"\n";}
template<typename T,typename... Args>void test(T x,Args... args){cerr<<x<<" ";test(args...);}
const LL mod=1e9+7;
const int maxn=3e5+9;
const int inf=0x3f3f3f3f;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
    while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans; return ans;
}
#define rd rd()
/*_________________________________________________________begin*/

int n,val[3];
LL a[maxn];
void Input(){
    n=rd;rep(i,0,2)val[i]=rd;
    rep(i,1,n)a[i]=rd;
}

typedef vector<vector<int>> node;
int SG[maxn][3];
bool vis[5];
map<node,int>ID;
int base,period;

void Begin(){
    node O(5,vector<int>(3,0));
    ID[O]=0;
    int p=1;
    while(1){
        rep(i,0,2){
            mmm(vis,0);
            rep(j,0,2){
                if(i&&j==i)continue;
                int x=max(0,p-val[j]);
                vis[SG[x][j]]=1;
            }
            int j=0;
            while(vis[j])j++;
            SG[p][i]=j;
        }
        node T(5);
        rep(i,0,3)T[i]=O[i+1];
        rep(i,0,2)T[4].pb(SG[p][i]);
        if(ID.count(T)){
            period=p-ID[T];
            base=p;
            return;
        }
        ID[T]=p;
        O=T;
        p++;
    }
}

LL getSG(LL p,int j){
    if(p<=base)return SG[p][j];
    LL len=(p-base+period-1)/period;
    p-=len*period;
    return SG[p][j];
}

void Deal(){
    int Sg=0;
    rep(i,1,n){
        Sg^=getSG(a[i],0);
    }
    int ans=0;
    rep(i,1,n){
        Sg^=getSG(a[i],0);
        rep(j,0,2){
            LL x=max(0ll,a[i]-val[j]);
            if(!(Sg^getSG(x,j)))
                ans++;
        }
        Sg^=getSG(a[i],0);
    }
    printf("%d\n",ans);
}

void Init(){
    ID.clear();
}

int main(){
    int t=rd;
    while(t--){
        Init();
        Input();
        Begin();
        Deal();
    }
    return 0;
}
/*_________________________________________________________end*/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章