题目:https://nanti.jisuanke.com/t/24852
题目大意:给一个模式串和n个它的子串,Alice和Bob玩游戏,Alice先手,每回合任选一个子串,该回合轮到的人在它后面加一个字母,并且保证加了之后的新串仍然是模式串的子串。轮到后没办法保证上述添加要求的人输。
(虽然题目没有说,但是字符集是小写字母)
可以看出这其实是一个经典的n堆nim博弈,可以用每个子串的sg函数异或值来求。
问题就在于求sg函数需要枚举所有走法,然后再取不与走了之后状态sg值重复的最小非负。
Sam很好的提供了所有走法,只要能在自动机里走,就代表生成的新串仍然是模式串的子串。
并且因为他们采取最优走法,所以sam的终点(last)一定是每个给定子串最终的归宿(最终游戏局面)。
即sg[last] = 0。
那么就可以将自动机排序后从后往前求每个节点的sg,然后把给定子串走到对应状态,取出其sg值,全部异或后即是要求的最终sg。
这样做其实把这个游戏转换成了取石子,加字符实际上是从剩下可走的状态集(石子堆)中取字符。
jojo!我不做女装大佬啦,我在男人八题签到啦!!!!!!!
感谢sg-master权哥帮我复习sg函数。
ac代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100005;
int n;
char s[maxn];
char t[105][maxn];
struct Sam {
int next[maxn << 1][26];
int link[maxn << 1], step[maxn << 1];
int sg[maxn << 1], vis[30];
int a[maxn], b[maxn << 1];
int sz, last, len, root;
void init() {
for(int i = 0; i <= sz; i++) {
memset(next[i], 0, sizeof(next[i]));
}
memset(a, 0, sizeof(a));
root = sz = last = 1;
}
void add(int c) {
int p = last;
int np = ++sz;
last = np;
step[np] = step[p] + 1;
while(!next[p][c] && p) {
next[p][c] = np;
p = link[p];
}
if(p == 0) {
link[np] = root;
} else {
int q = next[p][c];
if(step[p] + 1 == step[q]) {
link[np] = q;
} else {
int nq = ++sz;
memcpy(next[nq], next[q], sizeof(next[q]));
step[nq] = step[p] + 1;
link[nq] = link[q];
link[q] = link[np] = nq;
while(next[p][c] == q && p) {
next[p][c] = nq;
p = link[p];
}
}
}
}
void build() {
init();
for(int i = 0; s[i] ; i++) {
add(s[i] - 'a');
}
for(int i = 1; i <= sz; i++) {
a[step[i]]++;
}
for(int i = 1; i <= step[last]; i++) {
a[i] += a[i - 1];
}
for(int i = 1; i <= sz; i++) {
b[a[step[i]]--] = i;
}
}
void grundy() {
sg[last] = 0;
for(int i = sz; i > 1; i--) {
int e = b[i];
int g = 0;
memset(vis, 0, sizeof(vis));
for(int j = 0; j < 26; j++) {
if(!next[e][j]) {
continue;
}
vis[sg[next[e][j]]] = 1;
}
while(vis[g]) {
++g;
}
sg[e] = g;
}
}
void solve() {
build();
grundy();
int sum = 0;
for(int i = 1; i <= n; i++) {
int p = root;
for(int j = 0; t[i][j] ; j++) {
p = next[p][t[i][j] - 'a'];
}
sum ^= sg[p];
}
printf("%s\n", sum ? "Alice" : "Bob");
}
} sam;
int main() {
while(~scanf("%s", s)) {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%s", t[i]);
}
sam.solve();
}
return 0;
}