Codeforces Round #556

A Stock Arbitraging

暴力

B Tiling Challenge

暴力

1A&C Prefix Sum Primes

先放2,再放一個1,之後把所有的2放完,再放1,除了2,其他的質數都是奇數

1B&D - Three Religions

序列自動機+dp
分析:給定三個空串和一個母串
操作1:選一個串在其後面增加一個字符
操作2:刪除一個串最後的一個字符
總結,關於序列匹配首先想到序列自動機肯定沒錯,關鍵是這個m^3的dp不好想,div2 最後也沒過幾個

分析:
dp[i][j][k]dp[i][j][k] 表示匹配i,j,ki,j,k 最少需要到母串的哪個位置

dp[i][j][k]=min(nxt[dp[i1][j][k]][s[i][0]a]),nxt[dp[i][j1][k]][s[j][1]a],nxt[dp[i][j][k1]][s[k][2]a]);dp[i][j][k] = min(nxt[dp[i-1][j][k]][s[i][0]-'a']),nxt[dp[i][j-1][k]][s[j][1]-'a'],nxt[dp[i][j][k-1]][s[k][2]-'a']);

const int maxn = 3e5+10,maxm = 250+10;
char ar[maxn],s[maxm][4];
int len[3];
int nxt[maxn][30];
int dp[maxm][maxm][maxm];

char op[3],ch[maxn];
int main(void)
{
    int n,m;cin>>n>>m;
    cin>>(ar+1);
    for(int i = 0;i < 26; ++i) nxt[n][i]  = nxt[n+1][i] = n+1;
    for(int i = n-1;i >= 0; --i){
      for(int j = 0;j < 26; ++j)
        nxt[i][j] = nxt[i+1][j];
      nxt[i][(int)(ar[i+1]-'a')] = i+1;
    }
    for(int i = 1;i <= m; ++i){
      int t;
      scanf("%s %d",op,&t);
      t--;
      // cout<<t<<endl;
      if(op[0] == '-'){
        len[t]--;
      }
      else{
        scanf("%s",ch);
        // cout<<len[t]<<endl;
        ++len[t];
        // cout<<len[t]<<endl;

        // cout<<len[0]<<" "<<len[1]<<" "<<len[2]<<endl;
        s[len[t]][t] = ch[0];
        // 初始化
        // DEBUG
        for(int i = t == 0?len[0]:0;i <= len[0]; ++i){
          for(int j = t == 1?len[1]:0;j <= len[1]; ++j){
            for(int k = t == 2?len[2]:0;k <= len[2]; ++k){
              // cout<<i<<" "<<j<<" "<<k<<endl;
              dp[i][j][k] = n+1;
            }
          }
        }
        // DEBUG;
        for(int i = t == 0?len[0]:0;i <= len[0]; ++i){
          for(int j = t == 1?len[1]:0;j <= len[1]; ++j){
            for(int k = t == 2?len[2]:0;k <= len[2]; ++k){
              if(i > 0)
                dp[i][j][k] = min(dp[i][j][k],nxt[dp[i-1][j][k]][s[i][0]-'a']);
              if(j > 0)
                dp[i][j][k] = min(dp[i][j][k],nxt[dp[i][j-1][k]][s[j][1]-'a']);
              if(k > 0)
                dp[i][j][k] = min(dp[i][j][k],nxt[dp[i][j][k-1]][s[k][2]-'a']);
            }
          }
        }
        
      }
      // cout<<dp[len[0]][len[1]][len[2]]<<endl;
      puts(dp[len[0]][len[1]][len[2]] <= n ?"YES":"NO");
    }


   return 0;
}

div1 E Election Promises

題意
給定一棵有向樹,每個點有一個權值,兩個人進行操作,可以選擇任意一個節點,將其權值減小爲一個非負數,並且可以將其子節點修改爲任意的權值,判定是否先手必贏,如果先手必贏,給定一個先手的方案

分析:

1 我們看到有向樹,就可以聯想到拓撲排序有關的問題,或者直接dfs進行圖的遍歷,

2 博弈問題,我們應該關注其是不是一個ICG(組合博弈問題),能否使用sg函數找出其規律並求解,當你找出了sg函數的時候,你就已經可以解決這個問題了

3 大部分博弈問題的模型都或多或少與石子博弈問題有關,本題同樣,輸出方案的過程就是石子博弈問題輸出方案的翻版

本題的SG函數模型,我們可以這樣來構造,即

一個節點的Sg函數值,就是mex(sg[son])

那麼判斷先手必勝的條件是什麼呢?,

1 我們首先想到博弈問題有關的樹博弈問題,根節點的sg值就是整個遊戲的sg值,這樣想,樣例就不對

2 將所有節點的值Sg函數值都異或起來,看起來挺有效的,但是也不太對

3 我們思考如果只有兩個點的情況,如果沒有邊,那麼就是石子博弈問題,直接取sumxor(h(x)) 就ok了,對於這一題,我們同樣需要異或,就是將所有相同sg值的點的h值異或,如果有一個異或值部位0,那麼肯定必贏

怎麼輸出方案呢?

首先我們參考有關問題的方案輸出,那就是我們找到最大的sg函數相同的異或不爲0的位置,如果 xorsum[sg[u]] ^ h[i] < h[i] ,說明這個時候就是可行的,它的子節點包含所有的sg值,可以同時將它的子節點的xor[sg[son]]都修改,這樣最後肯定所有 的xor[sg[i]] 都爲0,就是必敗態

const int maxn = 2e5+10;
vector<int> G[maxn];
int topo[maxn];
int  sg[maxn],deg[maxn],mem[maxn];
int  Xor[maxn];
LL  h[maxn];
int main(void)
{
    // IOS;
    int n,m;cin>>n>>m;
    for(int i = 1;i <= n; ++i) scanf("%lld",&h[i]);
        // cin>>h[i];
    for(int i = 1;i <= m; ++i){
        int u,v;
        //cin>>u>>v;
        scanf("%d%d",&u,&v);
        G[u].Pb(v);
        deg[v]++;
    }
    queue<int> q;
    int cnt = 1;
    for(int i = 1;i <= n; ++i)
        if(deg[i] == 0)
            q.push(i);//,vis[i] = true;
    while(!q.empty()){
        int p = q.front(); q.pop();
        topo[cnt++] = p;
        for(auto c: G[p]){
            deg[c]--;
            if(deg[c] == 0)
                q.push(c);
        }
    }

    for(int i = n;i >= 1; --i){
        int  u = topo[i];
        for(auto c: G[u])
            mem[sg[c]] = i;
        while(mem[sg[u]] == i)
            sg[u]++;
        Xor[sg[u]] ^= h[u];
    }
    // for(int i = 1;i <= n; ++i)
    //     cout<<sg[i]<<" ";
    // cout<<endl;
    int index = -1;
    for(int i = 0;i < maxn; ++i)
        if(Xor[i] != 0)
            index = i;
    if(index == -1)
        return 0*puts("LOSE");
    for(int i = 1;i <= n; ++i){

        if(sg[i] == index && ((h[i] ^ Xor[index]) < h[i])){
            h[i] ^= Xor[index];
            for(auto c: G[i]){
                h[c] ^= Xor[sg[c]];
                Xor[sg[c]] = 0;
            }
            break;
        }
    }
    // for
    puts("WIN");
    for(int i = 1;i <= n; ++i)
        printf("%lld ",h[i]);
    

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