一开始以为是AC自动机的题,发现一直不会求T的AC自动机。后来想到可以求S的sa,就发现简单多了!
我们可以dfs S,然后对于S中的一个节点处理它的询问。(二分+hash比较大小)
那么问题就在于怎么维护T的hash。本来想用可持久化块链,发现mle了。就只好学了下treap。
treap的话,如果把rank看成下标,把随机的值看成权值的话,其实就是一颗随机数列上的笛卡尔树。树高等于比前面的数都大的数的个数的期望数量,那么在位置i的数被选的概率是
1i
,所以树高就是调和级数,就是
O(lnn)
。
treap有一个非常好的性质就是树的形态是确定的。而且如果两棵treap满足一棵树完全小于另一棵树,那么它们就是可以merge的,merge起来就是跟左偏树一样就可以了。
然后发现这道题的操作就是完全的裸merge。。
本来写的普通的hash被成功卡常了。。改成自然溢出就跑得飞快。
代码:
#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cassert>
const int Q=3e5+5;
const int S=1e5+5,T=3e5+5;
const int Len=1e5+5;
const int Sigma=26;
const int inf=1e5+1;
int n=1;
int a[S];
int stack[S],top;
typedef long long LL;
const int base=317;
int power[Len];
int next[S],succ[S],ptr[S],etot=1;
void addedge(int from,int to){
//printf("Addedge(%d)=(%d,%d)\n",etot,from,to);
next[etot]=ptr[from],ptr[from]=etot,succ[etot++]=to;
}
const int Log=16;
int fa[S],depth[S];
int q[S];
int shash[S];
void bfs(){
q[0]=1;
for(int h=0,t=1;h!=t;++h){
shash[q[h]]=shash[fa[q[h]]]*base+a[q[h]];
depth[q[h]]=depth[fa[q[h]]]+1;
for(int i=ptr[q[h]];i;i=next[i]){
fa[succ[i]]=q[h];
q[t++]=succ[i];
}
}
}
int cnts[S];
int sa[S],nextsa[S];
int rank[S],nextrank[S];
vector<int> son[S];
void build_sa(){
for(int i=n;i;--i)++cnts[rank[i]=a[i]];
for(int i=1;i<=n;++i)cnts[i]+=cnts[i-1];
for(int i=n;i;--i)sa[cnts[rank[i]]--]=i;
bool flag;
for(;;){
flag=1;
for(int i=n;i;--i)
if(fa[i]){
flag=0;
break;
}
if(flag)break;
/*printf("sa=\n");
for(int i=1;i<=n;++i)printf("%d:%d\n",i,sa[i]);
printf("rank=\n");
for(int i=1;i<=n;++i)printf("%d:%d\n",i,rank[i]);
puts("");*/
memset(cnts,0,sizeof(cnts));
for(int i=n;i;--i)++cnts[rank[i]];
for(int i=1;i<=n;++i)cnts[i]+=cnts[i-1];
for(int i=n;i;--i)son[i].clear();
for(int i=n;i;--i)
if(fa[i])
son[fa[i]].push_back(i);
for(int i=n;i;--i)
for(int j=son[sa[i]].size();j--;){
//printf("Get %d at %d\n",j,sa[i]);
nextsa[cnts[rank[son[sa[i]][j]]]--]=son[sa[i]][j];
}
for(int i=n;i;--i)
if(!fa[i])
nextsa[cnts[rank[i]]--]=i;
memcpy(sa,nextsa,sizeof(sa));
flag=1;
for(int i=1;i<=n;++i)
if(rank[sa[i]]==rank[sa[i-1]]&&rank[fa[sa[i]]]==rank[fa[sa[i-1]]]){
nextrank[sa[i]]=nextrank[sa[i-1]];
flag=0;
}
else nextrank[sa[i]]=i;
memcpy(rank,nextrank,sizeof(rank));
if(flag)break;
for(int i=n;i--;)fa[q[i]]=fa[fa[q[i]]];
}
/*printf("sa=\n");
for(int i=1;i<=n;++i)printf("%d:%d\n",i,sa[i]);
printf("rank=\n");
for(int i=1;i<=n;++i)printf("%d:%d\n",i,rank[i]);
puts("");*/
}
int gethash(int spos,int l){
return spos-l<0?-1:shash[stack[spos]]-shash[stack[spos-l]]*power[l];
}
int thash[T],size[T];
struct TS{
int ch[2];
int c;
int key;
int hash;
int size;
}bst[10000000];
int root[T];
int btot=1;
int rand_32(){
return rand()<<17^rand()<<10^rand();
}
void out(int node){
printf("treap(%d)={ch[0]=%d,ch[1]=%d,c=%d,hash=%d,size=%d}\n",node,bst[node].ch[0],bst[node].ch[1],bst[node].c,bst[node].hash,bst[node].size);
}
int newnode(int hash){
bst[btot]=(TS){0,0,rand_32(),hash,1};
return btot++;
}
void pushup_treap(int node){
bst[node].hash=bst[bst[node].ch[1]].hash+bst[node].c*power[bst[bst[node].ch[1]].size]+bst[bst[node].ch[0]].hash*power[bst[bst[node].ch[1]].size+1];
bst[node].size=bst[bst[node].ch[0]].size+bst[bst[node].ch[1]].size+1;
//printf("%d(%d)->%d\n",bst[bst[node].ch[1]].hash,bst[bst[node].ch[1]].size,bst[node].hash);
}
void merge(int &node,int u,int v){
//cout<<"Merge("<<u<<','<<v<<")\n";
if(!u&&!v){
node=0;
return;
}
node=btot++;
if(!u){
bst[node]=bst[v];
//pushup_treap(node);
//out(node);
return;
}
if(!v){
bst[node]=bst[u];
//pushup_treap(node);
//out(node);
return;
}
if(bst[u].key>bst[v].key){
bst[node]=bst[u];
merge(bst[node].ch[1],bst[u].ch[1],v);
}
else{
bst[node]=bst[v];
merge(bst[node].ch[0],u,bst[v].ch[0]);
}
pushup_treap(node);
//out(node);
}
int cmp(int spos,int node){
//printf("---cmp(%d,%d)--\n",stack[spos],node);
int ans;
for(;node;){
//cout<<"Gethash("<<stack[spos]<<","<<bst[bst[node].ch[1]].size+1<<")\n";
//printf("hash(%d)=%d\n",node,(bst[node].c*power[bst[bst[node].ch[1]].size+1]+bst[bst[node].ch[1]].hash)%Mod);
//out(node);
if(gethash(spos,bst[bst[node].ch[1]].size+1)==bst[node].c*power[bst[bst[node].ch[1]].size]+bst[bst[node].ch[1]].hash){
spos-=bst[bst[node].ch[1]].size+1;
node=bst[node].ch[0];
//cout<<"into left\n";
}
else{
ans=node;
node=bst[node].ch[1];
//cout<<"into right\n";
}
}
//printf("=%d\n",a[stack[spos]]<bst[ans].c?-1:1);
return a[stack[spos]]<bst[ans].c?-1:1;
}
int compare(int qs,int qt){
//printf("compare(%d,%d)\n",qs,qt);
if(gethash(qs=depth[qs]-1,size[qt])==thash[qt])return 0;
else return cmp(qs,root[qt]);
}
struct SS{
int l,r;
int size;
}segt[S<<2];
#define lson node<<1,l,l+r>>1
#define rson node<<1|1,(l+r>>1)+1,r
int f[S],caltime[S];
void pushup_seg(int node){
segt[node]=(SS){min(segt[node<<1].l,segt[node<<1|1].l),max(segt[node<<1].r,segt[node<<1|1].r),segt[node<<1].size+segt[node<<1|1].size};
}
void build_seg(int node,int l,int r){
//printf("build_seg(%d,%d,%d)\n",node,l,r);
segt[node].l=inf;
if(l!=r)build_seg(lson),build_seg(rson);
}
void update(int node,int l,int r,int x){
if(l==r){
if(segt[node].r)segt[node]=(SS){inf,0,0};
else segt[node]=(SS){l,l,1};
return;
}
if(x<=l+r>>1)update(lson,x);
else update(rson,x);
pushup_seg(node);
}
void recover(int node){
if(caltime[segt[node].l]==node)caltime[segt[node].l]=0;
if(caltime[segt[node].r]==node)caltime[segt[node].r]=0;
}
int query(int node,int l,int r,int qt){
//printf("query(%d,%d,%d,%d)\n",node,l,r,qt);
if(segt[node].r==0)return 0;
if(caltime[segt[node].l]==0){
caltime[segt[node].l]=node;
f[segt[node].l]=compare(sa[segt[node].l],qt);
}
if(f[segt[node].l]==1){
recover(node);
return 0;
}
if(caltime[f[segt[node].r]]==0){
caltime[segt[node].r]=node;
f[segt[node].r]=compare(sa[segt[node].r],qt);
}
if(f[segt[node].r]==-1){
recover(node);
return 0;
}
if(f[segt[node].l]==0&&f[segt[node].r]==0){
recover(node);
return segt[node].size;
}
else{
int ans=query(lson,qt)+query(rson,qt);
recover(node);
return ans;
}
}
struct QS{
int t,i;
};
vector<QS> que[S];
int ans[Q];
int cur[S];
void dfs(){
for(int i=n;i;--i)cur[i]=ptr[i];
for(stack[top++]=1;top--;){
if(cur[stack[top]]==ptr[stack[top]]){
//printf("First visit %d in the stack\n",stack[top]);
//printf("add_seg(%d at %d)\n",stack[top],rank[stack[top]]);
update(1,1,n,rank[stack[top]]);
for(int i=que[stack[top]].size();i--;){
//cout<<i<<endl;
//printf("Get %d at %d\n",que[stack[top]][i].i,stack[top]);
//cout<<(size[que[stack[top]][i].t]==inf||root[que[stack[top]][i].t]==0)<<endl;
if(size[que[stack[top]][i].t]==inf||root[que[stack[top]][i].t]==0)ans[que[stack[top]][i].i]=0;
else ans[que[stack[top]][i].i]=query(1,1,n,que[stack[top]][i].t);
}
}
if(cur[stack[top]]){
stack[top+1]=succ[cur[stack[top]]];
//cout<<cur[stack[top]]<<"->"<<next[cur[stack[top]]]<<endl;
cur[stack[top]]=next[cur[stack[top]]];
top+=2;
}
else update(1,1,n,rank[stack[top]]);
}
}
char * cp=(char *)malloc(5000000);
void in(int &x){
while(*cp<'0'||*cp>'9')++cp;
for(x=0;*cp>='0'&&*cp<='9';)x=x*10+(*cp++^'0');
}
void in(char &c){
while(*cp<'a'||*cp>'z')++cp;
c=*cp++;
}
int main(){
//freopen("bzoj_2787.in","r",stdin);
//freopen("bzoj_2787.out","w",stdout);
fread(cp,1,5000000,stdin);
power[0]=1;
for(int i=1;i<=1e5+2;++i)power[i]=power[i-1]*base;
int q;
in(q);
int opt;
int u,v;
char c;
int ttot=2;
for(int i=0;i<q;++i){
//printf("---%d---\n",i);
//if(i==200000)return 0;
in(opt);
switch(opt){
case 1:
in(u),in(c);
a[++n]=c-'a'+1;
addedge(u,n);
break;
case 2:
in(opt),in(u),in(c);
if(size[u]+1<inf){
c+=-'a'+1;
bst[btot]=(TS){0,0,c,rand_32(),c,1};
//printf("newnode:");
//out(btot);
if(opt==0){
merge(root[ttot],btot++,root[u]);
thash[ttot]=c*power[size[u]]+thash[u];
}
else{
merge(root[ttot],root[u],btot++);
thash[ttot]=thash[u]*base+c;
}
}
size[ttot++]=min(size[u]+1,inf);
break;
case 3:
in(u),in(v);
if(size[u]+size[v]<inf){
merge(root[ttot],root[u],root[v]);
thash[ttot]=thash[u]*power[size[v]]+thash[v];
}
size[ttot++]=min(size[u]+size[v],inf);
break;
case 4:
in(u),in(v);
que[v].push_back((QS){u,i});
//cout<<"Add:"<<v<<endl;
break;
}
}
bfs();
build_sa();
build_seg(1,1,n);
memset(ans,-1,sizeof(ans));
//printf("root=\n");
//for(int i=1;i<ttot;++i)printf("%d:%d\n",i,root[i]);
//printf("shash=\n");
//for(int i=1;i<=n;++i)printf("%d:%d\n",i,shash[i]);
//printf("thash=\n");
//for(int i=1;i<ttot;++i)printf("%d:%d\n",i,thash[i]);
//puts("");
dfs();
for(int i=0;i<q;++i)
if(~ans[i])
printf("%d\n",ans[i]);
}
总结:
①ac自动机与sa很多时候能做相同的事情,如果觉得其中一个做起来困难,可以考虑另一个。