标签:树状 %s using ret while fail树 lca names .so
这道题利用了fail树的神奇性质————父节点为其子节点的前缀
先对Alice的集合建一个fail树,
Bob每插入一个串,都将串在自动机上经过的点在树上打上标记(+1)
每次查询的答案就是询问串的结束节点的子树的贡献
所以还需要用到树状数组来维护dfs序
因为Bob的一个串至多只能对Alice的某一个串做出1的贡献,所以不能单纯加和
要对树链取并
可以将一个Bob串在fail树上经过的所有点按入栈顺序排序,相邻每两个点的lca贡献-1
可以证明,这样会使每次某个点的子树内没有重复贡献
1 #include<queue> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 int n,m,cnt,tot,tp,num; 7 char s[2000005]; 8 bool ed[2000005]; 9 int vis[2000005]; 10 int ed_pos[100005]; 11 int head[2000005]; 12 int fa[2000005]; 13 int dep[2000005]; 14 int grand[2000005]; 15 int siz[2000005]; 16 int son[2000005]; 17 int ad[4000005]; 18 int stk[4000005]; 19 int tmp[2000005]; 20 struct Trie{ 21 int son[26]; 22 int fail; 23 }tr[2000005]; 24 struct Edge{ 25 int fr; 26 int to; 27 int nxt; 28 }edge[2000005]; 29 struct node{ 30 int in; 31 int out; 32 }pos[2000005]; 33 int lowbit(int x){ 34 return x&(-x); 35 } 36 void init(){ 37 memset(head,-1,sizeof(head)); 38 } 39 void addx(int x,int v){ 40 while(x<=2*tot){ 41 ad[x]+=v; 42 x+=lowbit(x); 43 } 44 } 45 int getsum(int x){ 46 int ret=0; 47 while(x){ 48 ret+=ad[x]; 49 x-=lowbit(x); 50 } 51 return ret; 52 } 53 void addedge(int u,int v){ 54 edge[cnt].fr=u; 55 edge[cnt].to=v; 56 edge[cnt].nxt=head[u]; 57 head[u]=cnt++; 58 } 59 void insert(char h[],int mrk){ 60 int now=0; 61 int len=strlen(h+1); 62 for(int i=1;i<=len;i++){ 63 int k=h[i]-‘a‘; 64 if(!tr[now].son[k])tr[now].son[k]=++tot; 65 now=tr[now].son[k]; 66 } 67 ed[now]=true; 68 ed_pos[mrk]=now; 69 } 70 void getfail(){ 71 queue<int>que; 72 for(int i=0;i<26;i++){ 73 if(tr[0].son[i]){ 74 addedge(0,tr[0].son[i]); 75 que.push(tr[0].son[i]); 76 } 77 } 78 while(!que.empty()){ 79 int u=que.front(); 80 que.pop(); 81 for(int i=0;i<26;i++){ 82 if(tr[u].son[i]){ 83 tr[tr[u].son[i]].fail=tr[tr[u].fail].son[i]; 84 addedge(tr[tr[u].fail].son[i],tr[u].son[i]); 85 que.push(tr[u].son[i]); 86 } 87 else tr[u].son[i]=tr[tr[u].fail].son[i]; 88 } 89 } 90 } 91 void dfs1(int u){ 92 siz[u]=1;stk[++tp]=u;pos[u].in=tp; 93 for(int i=head[u];i!=-1;i=edge[i].nxt){ 94 int v=edge[i].to; 95 if(v==fa[u])continue; 96 fa[v]=u;dep[v]=dep[u]+1; 97 dfs1(v);siz[u]+=siz[v]; 98 if(siz[v]>siz[son[u]]||(!son[u]))son[u]=v; 99 } 100 stk[++tp]=u;pos[u].out=tp; 101 } 102 void dfs2(int u){ 103 if(u!=son[fa[u]])grand[u]=u; 104 else grand[u]=grand[fa[u]]; 105 for(int i=head[u];i!=-1;i=edge[i].nxt){ 106 int v=edge[i].to; 107 if(v==fa[u])continue; 108 dfs2(v); 109 } 110 } 111 int lca(int x,int y){ 112 while(grand[x]!=grand[y]){ 113 if(dep[grand[x]]<dep[grand[y]])swap(x,y); 114 x=fa[grand[x]]; 115 } 116 if(dep[x]>dep[y])swap(x,y); 117 return x; 118 } 119 int cmp(int a,int b){ 120 return pos[a].in<pos[b].in; 121 } 122 void match(char b[],int tim){ 123 int now=0; 124 int len=strlen(s+1); 125 for(int i=1;i<=len;i++){ 126 int k=b[i]-‘a‘; 127 now=tr[now].son[k]; 128 if(vis[now]==tim)continue; 129 vis[now]=tim;tmp[++num]=now; 130 } 131 sort(tmp+1,tmp+num+1,cmp); 132 for(int i=1;i<=num;i++)addx(pos[tmp[i]].in,1); 133 for(int i=1;i<num;i++){ 134 int f=lca(tmp[i],tmp[i+1]); 135 addx(pos[f].in,-1); 136 } 137 } 138 int main(){ 139 init(); 140 scanf("%d",&n); 141 for(int i=1;i<=n;i++){ 142 scanf("%s",s+1); 143 insert(s,i); 144 } 145 getfail();dfs1(0);dfs2(0); 146 scanf("%d",&m); 147 for(int i=1;i<=m;i++){ 148 int ac; 149 scanf("%d",&ac); 150 if(ac==1){ 151 scanf("%s",s+1); 152 num=0;match(s,i); 153 }else{ 154 int p; 155 scanf("%d",&p); 156 int ans=getsum(pos[ed_pos[p]].out)-getsum(pos[ed_pos[p]].in-1); 157 printf("%d\n",ans); 158 } 159 } 160 return 0; 161 }
bzoj[3881]Divljak(dfs序+树状数组+fail树)
标签:树状 %s using ret while fail树 lca names .so
原文地址:https://www.cnblogs.com/lnxcj/p/10021072.html