BZOJ-3881 Divljak
听说是2015论文题。。。《浅谈字符串匹配的几种方法》例3 在P41
对于Alice的串先插到AC自动机里面,然后用Bob的串来贡献答案。
考虑什么时候贡献了答案,一个串P能贡献给Sx答案 当且仅当Bob的P串能(沿匹配边或fail边)走到Alice的Sx串终止位置。
所以有一种暴力的做法是,对于新加进来的某个节点,我们暴力沿着这个节点跳fail,如果没更新就更新一下,这样做的复杂度应该可以卡到平方$ n^{4/3}$ 级别。
考虑优化暴力。我上网搜了一下都是一些树链求并的题解,求LCA,dfs序,树状数组,然后T了2333真是喜闻乐见好像论文里就是这种写法。。。
利用树链的并 将所有要处理的节点按照dfs序排序,然后首先把它们到根的路径+1 随后将相邻两个节点的LCA到根的路径-1 非常科学 怎么加?树链剖分?等TLE吧 只需用树状数组维护DFS序即可 可惜卡常数! 用倍增LCA会超时 于是我们换用RMQlca,并用ZKW线段树维护 估计ST表预处理也会超时
然后Orz了kfdong,发现LCT可以直接上!整个人都惊呆了
我的access代码大概长这样:
void access(int t,int las=0){ for(;t;splay(t),son[t][1]=las,las=t,t=fa[t]); }
LCT求LCA:假设求lca(x,y),那么access(x),access(y),做完y以后的las就是LCA。
对于这道题而言,我们求出fail树,然后在上面维护两个标记,一个是时间戳,另一个是增量。
不是要把树链并起来吗?还是用access,只不过多访问一次时间戳。如果节点t之前的点已经在这一个时间段更新过了,那么就停止access,然后打标记。这样就可以不用容斥了~
然后写一下交一发,发现跑了8s+(我机子慢一个测试点开了O2要跑24s+。。。。真是一个悲伤的故事)
还有什么地方可以优化的呢?
再次Orz了AKF的代码以后,发现他有一个优化,缩点!将不是danger标记的节点统统删掉,这样的话点集规模直接变成了n,然后剩下的该怎么做就怎么做,一下子跑进了4s
试了试IO,好像数据有问题还是自己写龊了会RE。于是就扔在一边不管了。。。
/************************************************************** Problem: 3881 User: wjy1998 Language: C++ Result: Accepted Time:3496 ms Memory:242856 kb ****************************************************************/ #include<cstdio> #include<algorithm> #define N 1620000 int n,m,i,j,fail[N],ch[N][26],tot,las,end[N],q[N],danger[N],id[N];char s[N]; int fa[N],son[N][2],add[N],cnt[N],tim[N],vis[N],tc; #define ls son[t][0] #define rs son[t][1] #define nr(t) (son[fa[t]][0]==t||son[fa[t]][1]==t) void pd(int t){if(!t)return; if(add[t]){ add[ls]+=add[t],add[rs]+=add[t]; cnt[ls]+=add[t],cnt[rs]+=add[t];add[t]=0; } if(tim[ls]<tim[t]||vis[ls]<tim[t])vis[ls]=tim[ls]=tim[t]; if(tim[rs]<tim[t]||vis[rs]<tim[t])vis[rs]=tim[rs]=tim[t]; } void rtt(int t){ int f=fa[t],p=son[f][1]==t; fa[t]=fa[f],nr(f)?son[fa[f]][son[fa[f]][1]==f]=t:1; (son[f][p]=son[t][p^1])?fa[son[f][p]]=f:1,son[fa[f]=t][p^1]=f; } void pv(int t){if(nr(t))pv(fa[t]);pd(t);} void splay(int t){int f; for(pv(t);nr(t);rtt(t)) if(f=fa[t],nr(f))rtt(son[f][1]==t^son[fa[f]][1]==f?t:f); } void access(int t){int las=0; for(;t&&(splay(t),vis[t]<tc);rs=las,las=t,t=fa[t]); if(las&&vis[las]<tc)tim[las]=vis[las]=tc,add[las]++,cnt[las]++,splay(las); } void build_AC(){ int l,r,i,j,cnt=1; for(q[l=r=0]=0;l<=r;l++) for(i=0;i<26;i++) if(j=ch[q[l]][i]){ if(q[l])fail[j]=ch[fail[q[l]]][i]; q[++r]=j; } else ch[q[l]][i]=ch[fail[q[l]]][i]; for(id[0]=i=1;i<=r;i++) if(danger[q[i]]){ id[q[i]]=++cnt; fa[cnt]=id[fail[q[i]]]; } else id[q[i]]=id[fail[q[i]]]; } int main(){ scanf("%d\n",&n); for(i=1;i<=n;i++){ scanf("%s",s); for(las=j=0;s[j];j++){ s[j]-='a'; if(!ch[las][s[j]])ch[las][s[j]]=++tot; las=ch[las][s[j]]; } end[i]=las;danger[las]=1; } build_AC(); for(scanf("%d",&m);m--;){ scanf("%s",s); if(s[0]=='1'){ scanf("%s",s);tc++; for(las=i=0;s[i];i++){ las=ch[las][s[i]-'a']; access(id[las]); } } else{ scanf("%d",&i);i=id[end[i]]; pv(i);printf("%d\n",cnt[i]); } } }
我并没写过树链剖分但是想想 好像不能用在这里?