Jzoj P5912 VanUSee___博弈+KMP
阿新 • • 發佈:2018-12-16
題目大意:
給定兩個串 S 和 T,。 兩個人博弈,每一輪操作,兩人先後可以刪掉 S 的首位或者末位。 當操作以後的串的長度等於時,遊戲停止。 如果停止時 ,則後手獲勝,否則先手獲勝。 問兩人均採取最優策略下誰贏
分析:
考慮簡化表示狀態 設左邊已經刪掉了 L 個字元,右邊已經刪掉了 R 個字元 那麼用 R-L 來表示當前狀態,可以用 KMP 求出有哪些目標狀態 一開始 R-L 為 0,每一次操作可以讓它+1 或者-1,雙方輪流操作,總共|S|-|T|次操 作,雙方都用最優策略看最後是否能到達目標狀態 顯然所有的目標狀態奇偶性相同 當|S|-|T|為奇數時,最後一次操作是先手做,它肯定往不是目標狀態走,那麼一個 位置在最後一次操作前是目標狀態當且僅當它+1 它-1 都是目標狀態 現在就全部轉化成|S|-|T|為偶數的情況 假如 0 是目標狀態,那麼顯然後手會贏,因為無論先手往哪裡走後手都可以把他拉 回來 如果 0 不是,並且-2 和 2 不全是,那麼先手一定會朝不是的那一邊走,後手無論 如何都沒有辦法將它拉回到是的那一邊了 因此後手會贏當且僅當 0 是目標狀態或者-2 和 2 都是目標狀態 否則都是先手贏 利用KMP求出目標狀態的所有位置然後判斷。
程式碼:
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <queue> #include <cmath> #define N 100005 using namespace std; int begind[2][N], next[N], Q; char S[N], T[N]; int main() { freopen("vanusee.in", "r", stdin); freopen("vanusee.out", "w", stdout); scanf("%d", &Q); while (Q--) { scanf("%s", S + 1); int len1 = strlen(S + 1); scanf("%s", T + 1); int len2 = strlen(T + 1); memset(begind, 0, sizeof(begind)); int j = 0, tot = 0, orz = (len1 - len2) % 2; for (int i = 2; i <= len2; i++) { while (j && T[i] != T[j + 1]) j = next[j]; if (T[i] == T[j + 1]) j++; next[i] = j; } j = 0; for (int i = 1; i <= len1; i++) { while (j && S[i] != T[j + 1]) j = next[j]; if (S[i] == T[j + 1]) j++; if (j == len2) begind[orz][++tot] = (len1 - i) - (i - len2), j = next[j]; } if (orz) { sort(begind[orz] + 1, begind[orz] + tot + 1); for (int i = 2; i <= tot; i++) if (begind[orz][i] - begind[orz][i - 1] == 2) begind[orz][i - 1] = begind[orz][i] - 1; tot--; } bool xx = 0, yy = 0, xy = 0; for (int i = 1; i <= tot; i++) { if (begind[orz][i] == 2) xx = 1; if (begind[orz][i] == -2) yy = 1; if (begind[orz][i] == 0) xy = 1; } if (xy || (xx && yy)) printf("pty\n"); else printf("cqf\n"); } return 0; }