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

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

题意:

给出n个数,有3种攻击方式,分别是选择一个正数减去x,y,z
n 3 e 5 , a i 1 e 18 , x , y , z 5 n\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 [ i 5 , i 1 ] i\to [i-5,i-1]

单个状态无非是SG[i][j]表示值为i上次攻击为j。然后将 S G [ i 4 , 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*/

发布了773 篇原创文章 · 获赞 345 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/105095751