1. 程式人生 > >【2016北京集訓】魔法遊戲

【2016北京集訓】魔法遊戲

else lse 整數 滿足 理解 class 每一個 portal 語文

Portal --> broken qwq

Description

  初始的時候給你一棵樹,每個節點上有一個正整數,這棵樹經過一些操作之後可能變成一個森林,兩個人輪流選擇當前森林中的一棵樹的樹根,將該節點上面的數\(K\)變成\([K/a]\)(下取整,\(a\in [2,K+1]\)),當一個節點上的數變成\(0\)之後,這個節點會消失然後其所有兒子都會變成新的樹根,如果一個人操作後森林中沒有節點了那麽該玩家獲勝,求先手(\(Alice\))還是後手(\(Marisa\))有必勝策略

?  數據範圍:\(n<=100000\),節點上的數不超過\(2^{63}\)
  

Solution

?  首先你的語文要好才能做出這題qwq註意數據範圍是不超過

,所以我們要用unsigned long long。。

?  然後考慮轉化一下問題(所以為什麽我想到怎麽轉化但是不會做==)

?  我們可以把問題轉成一個取石子的模型,因為每個點至多操作\(log_2val+1\)次,然後。。因為\(a\in [2,val+1]\)所以。。總能讓這個新的\(val\)(記為\(val'\))滿足\(log_2val'+1\)等於一個小於\(log_2val+1\)的數,並且任意一個這個範圍內的數都可以取到,所以。。。我們就看成一個節點上有\(log_2val\)個石子,每次都要取一個根節點上的至少一顆石子這樣就好了

  然後。。接下來的事情就是。。\(sg\)

函數

?  根據\(sg\)定理,一個局面的\(sg\)值為其拆成的子遊戲的\(sg\)值的異或和,考慮當\(val=0\)的情況,這個節點的\(sg\)值應該就是其兒子的\(sg\)值異或和(因為。。\(val=0\)就相當其每一個兒子都是它拆出來的一個子遊戲,而一個人在其中一個兒子的樹中操作並不會影響到別的兒子的樹,所以它們是相互獨立的(一開始我的理解是錯的,並不是說不能中途對其他子遊戲操作,而是兩個子遊戲互不影響即算為獨立)

  然後我們就可以得到\(val=0\)時的\(sg\)值,接下來要用的就是\(sg\)的另一個求法:一個局面的\(sg\)值是其後繼局面的\(sg\)值的\(mex\)

,所以我們先取一次\(mex\)得到\(val=1\)的時候的\(sg\)值,再\(mex\)一次得到\(val=2\)的,以此類推。。然後我們就可以得到這個節點實際上的\(sg\)值啦

?  最後我們根據\(sg[1]\)判斷先手獲勝還是後手獲勝即可

?   

?  代碼大概長這個樣子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ull unsigned long long
using namespace std;
const int N=1e5+10;
struct xxx{
    int y,nxt;
}a[N*2];
int h[N],val[N],in[N],sg[N];
int vis[N];
int n,m,tot,T,mark;
void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;}
void init(){
    memset(h,-1,sizeof(h));
    tot=0;
    memset(sg,0,sizeof(sg));
}
void dfs(int fa,int x){
    int u,son=0,ret=0,cnt;
    for (int i=h[x];i!=-1;i=a[i].nxt){
        u=a[i].y;
        if (u==fa) continue;
        ++son;
        dfs(x,u);
        ret^=sg[u];
    }
    ++mark;
    vis[ret]=mark;
    cnt=0;
    sg[x]=0;
    for (int i=1;i<=val[x];++i){
        while (vis[sg[x]]==mark) ++sg[x];
        vis[sg[x]]=mark;
    }
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
#endif
    ull tmp;
    int x,y;
    while (scanf("%d",&n)!=EOF){
        for (int i=1;i<=n;++i){
            cin>>tmp;
            val[i]=(int)(log(1.0*tmp)/log(2.0))+1;
        }
        init();
        for (int i=1;i<n;++i){
            scanf("%d%d",&x,&y);
            ++x; ++y;
            add(x,y); add(y,x);
        }
        dfs(0,1);
        if (sg[1]) printf("Alice\n");
        else printf("Marisa\n");
    }
}

【2016北京集訓】魔法遊戲