题意
给n个单词,现在A和B进行博弈,A先手
轮流取一个单词,满足这个单词不为之前取过的某个单词的前缀
问A是否能赢,第一个取得单词可以是那些
读入按字典序给
样例
input:
5 9
ac
car
care
careful
carefully
output:
careful
串总长1e5 1s
假装建ac自动机
将每个串向读入中的最长前缀认爸爸
形成一个森林
博弈内容变为每次删掉一个点到根的路径,儿子分开,不能删者输
用SG函数,对x的子树进行博弈胜负状态用 S G ( x ) SG(x) SG(x)表示, S G ( ∅ ) = 1 SG(\empty)=1 SG(∅)=1
只对一棵树考虑,删掉一个点到根路径,子状态一定是若干棵树
记 a n c ( u ) anc(u) anc(u)为u与u的所有祖先,删掉 a n c ( u ) anc(u) anc(u)所有点。
裂成的子树的根集合是 s e p ( u ) = { b ∣ f a ( b ) = a , a ∈ a n c ( u ) , b ̸ ∈ a n c ( u ) } sep(u)=\{b|fa(b)=a,a\in anc(u),b\not\in anc(u)\} sep(u)={b∣fa(b)=a,a∈anc(u),b̸∈anc(u)}
这个子状态的SG函数就是 S G ( s e p ( u ) ) = ⨁ v ∈ s e p ( u ) S G ( v ) SG(sep(u))=\bigoplus_{v\in sep(u)}SG(v) SG(sep(u))=⨁v∈sep(u)SG(v)
那么一棵树x的SG值可以用下式求 S G ( x ) = m e x { S G ( u ) ∣ u ∈ s u b t r e e ( x ) } SG(x)=mex\{SG(u)|u\in subtree(x)\} SG(x)=mex{SG(u)∣u∈subtree(x)}
因此,我们用字典树 t r i e ( x ) trie(x) trie(x)记下所有 s e p x ( u ) sep_x(u) sepx(u)
初始情况下,trie(x)只有儿子的SG的异或
记 x o r ( x ) = ⨁ f a ( u ) = x S G ( u ) xor(x)=\bigoplus_{fa(u)=x}SG(u) xor(x)=⨁fa(u)=xSG(u)
那么 ∀ s ∈ t r i e ( x ) , s ⨁ x o r ( x ) ⨁ S G ( u ) ∈ t r i e ( f a ( x ) ) \forall_{s\in trie(x)},s\bigoplus xor(x) \bigoplus SG(u)\in trie(fa(x)) ∀s∈trie(x),s⨁xor(x)⨁SG(u)∈trie(fa(x))
就是说每往上走,就将trie里每个数异或上fa(x)除x之外的所有儿子SG值的异或,然后将trie合并到fa(x)上
SG(x)可以通过trie来求mex
合并不用启发式,可以参照线段树合并,如果在两棵树中,某个点都存在,就走下去合并,否则就只保留其中一棵,时间复杂度同线段树合并 O ( ∑ s i z e log ( ∑ s i z e ) ) O(\sum size\log (\sum size)) O(∑sizelog(∑size))
#include
#include
#include
#define N 100100
#define M 2000100
using namespace std;
int n,m,len[N],tag[M],sz[M],cnt,sn[M][2],nx[120],fir[N],nex[N],to[N],top,que[N],t,sg[N],p[N],pxr[N],q[N],rt[N];
char c[120],s[120],S[N][110];
#define link(u,v) to[++top]=v,nex[top]=fir[u],fir[u]=top
void Xor(int x,int v){
if(!x)return;
tag[x]^=v;
}
void down(int x,int f){
if(!x)return;
if(tag[x]){
int T=tag[x]&1<<f;
if(T)swap(sn[x][0],sn[x][1]);
Xor(sn[x][0],tag[x]-T);
Xor(sn[x][1],tag[x]-T);
tag[x]=0;
}
}
void ins(int &x,int f,int v){
if(!x)x=++cnt;
if(f<0){sz[x]=1;return;}
down(x,f);
int w=(v&1<<f)>>f;
ins(sn[x][w],f-1,v);
sz[x]=sz[sn[x][0]]+sz[sn[x][1]];
}
void merge(int &u,int v,int f){
if(!u||!v){u=u+v;return;}
if(f<0){sz[u]=1;return;}
down(u,f);down(v,f);
merge(sn[u][0],sn[v][0],f-1);
merge(sn[u][1],sn[v][1],f-1);
sz[u]=sz[sn[u][0]]+sz[sn[u][1]];
}
int mex(int x,int f){
if(!x)return 0;
down(x,f);
if(sz[sn[x][0]]<1<<f)return mex(sn[x][0],f-1);
return mex(sn[x][1],f-1)^1<<f;
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;++i){
char ch;int P=0,l=0;
for(;(ch=getchar())>'z'||ch<'a';);
for(;(ch>='a'&&ch<='z')||(ch>='0'&&ch<='9');ch=getchar()){
++l;S[i][l]=s[l]=ch;
if(l==P+1&&ch==c[l])P=l;
}if(nx[P])link(nx[P],i);else que[++t]=i,p[i]=1;
for(int j=P+1;j<=l;++j)nx[j]=nx[j-1],c[j]=s[j];nx[l]=i;
len[i]=l;c[l+1]=0;
}
for(int i=1;i<=t;++i)
for(int x=que[i],o=fir[x];o;o=nex[o])que[++t]=to[o];
for(int i=t;i;--i){
int x=que[i],xr=0;
for(int o=fir[x];o;o=nex[o])xr^=sg[to[o]];
ins(rt[x],16,xr);
for(int o=fir[x];o;o=nex[o]){
Xor(rt[to[o]],xr^sg[to[o]]);
merge(rt[x],rt[to[o]],16);
}sg[x]=mex(rt[x],16);
}int Xr=0;
for(int i=1;i<=n;++i)if(p[i])Xr^=sg[i];
if(!Xr)printf("Can't win at all!!");else{
int CNT=0;
for(int i=1;i<=n;++i)if(p[i]){
que[t=1]=i;pxr[1]=0;
for(int j=1;j<=t;++j){
int x=que[j],xr=0;
for(int o=fir[x];o;o=nex[o])xr^=sg[to[o]];
if((pxr[j]^xr^sg[i]^Xr)==0)q[x]=1;
for(int o=fir[x];o;o=nex[o])que[++t]=to[o],pxr[t]=pxr[j]^xr^sg[to[o]];
}
}
for(int i=1;i<=n;++i)if(q[i]){
for(int j=1;j<=len[i];++j){
++CNT;putchar(S[i][j]);
if(CNT==50)putchar('\n'),CNT=0;
}
}
}
}