bzoj 2434 [NOI2011] 阿狸的打字机
题面:
https://www.lydsy.com/JudgeOnline/problem.php?id=2434
题解:
建立AC自动机
把fail树拎出来,询问(x,y)就是在root到y的路径上,沿着fail能走到x的点有几个
那么就是fail树上x的子树中有几个root到y路径上的节点
我们遍历fail树,可以得到每个节点的进出时间l,r
然后遍历trie树,进入一个节点就在树状数组上这个节点的l值的位置+1,退出时就-1,当前节点如果是某一个字符串的end时,我们处理所有y是这个字符串的询问:
我们只要看子树里的和就行了,那么就查询l到r的和就是答案
Code:
1 #include<stdio.h> 2 #include<cstring> 3 #include<cstdlib> 4 #include<algorithm> 5 #include<vector> 6 #include<map> 7 #include<set> 8 #include<cmath> 9 #include<iostream> 10 #include<queue> 11 #include<string> 12 using namespace std; 13 typedef long long ll; 14 typedef pair<int,int> pii; 15 typedef long double ld; 16 typedef unsigned long long ull; 17 typedef pair<long long,long long> pll; 18 #define fi first 19 #define se second 20 #define pb push_back 21 #define mp make_pair 22 #define rep(i,j,k) for(register int i=(int)(j);i<=(int)(k);i++) 23 #define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--) 24 25 ll read(){ 26 ll x=0,f=1;char c=getchar(); 27 while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();} 28 while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();} 29 return x*f; 30 } 31 32 const int maxnode=150100; 33 const int maxn=100100; 34 35 #define lowbit(x) (x&-x) 36 struct BIT{ 37 int a[maxn*5]; 38 void add(int x,int val){ 39 while(x<=maxn*4){ 40 a[x]+=val; 41 x+=lowbit(x); 42 } 43 } 44 int ask(int x){ 45 int ret=0; 46 while(x){ret+=a[x];x-=lowbit(x);} 47 return ret; 48 } 49 } bit; 50 51 vector<pii> q[maxn]; 52 string s; 53 int l[maxn],r[maxn],tim; 54 int ans[maxn]; 55 int dy[maxn*4]; 56 int head[maxn*4],to[maxn*8],nxt[maxn*8],cnt; 57 int nums; 58 59 void addedge(int a,int b){ 60 to[++cnt]=b;nxt[cnt]=head[a];head[a]=cnt; 61 } 62 63 struct Trie{ 64 int nxt[maxnode][26],fail[maxnode],en[maxnode]; 65 int root,sz; 66 int fa[maxnode]; 67 int nxt2[maxnode][26]; 68 69 int newnode(){ 70 for(int i=0;i<26;i++) nxt[sz][i]=-1; 71 en[sz]=0;sz++; 72 return sz-1; 73 } 74 void init(){ 75 sz=0; 76 root=newnode(); 77 } 78 79 void doit(){ 80 getline(cin,s); 81 int nw=root; 82 for(int i=0;i<s.size();i++){ 83 if(s[i]=='P') en[nw]=++nums,dy[nums]=nw; 84 else if(s[i]=='B') nw=fa[nw]; 85 else{ 86 if(nxt[nw][s[i]-'a']==-1) nxt[nw][s[i]-'a']=newnode(); 87 fa[nxt[nw][s[i]-'a']]=nw; 88 nw=nxt[nw][s[i]-'a']; 89 } 90 } 91 } 92 void build(){ 93 for(int i=0;i<sz;i++) 94 for(int j=0;j<26;j++) 95 nxt2[i][j]=nxt[i][j]; 96 queue<int> q; 97 fail[root]=root; 98 for(int i=0;i<26;i++) 99 if(nxt[root][i]==-1) nxt[root][i]=root; 100 else{ 101 fail[nxt[root][i]]=root; 102 q.push(nxt[root][i]); 103 } 104 while(!q.empty()){ 105 int nw=q.front();q.pop(); 106 for(int i=0;i<26;i++) 107 if(nxt[nw][i]==-1) nxt[nw][i]=nxt[fail[nw]][i]; 108 else{ 109 if(nxt[nw][i]==0){ 110 cout<<nw<<endl; 111 break; 112 } 113 fail[nxt[nw][i]]=nxt[fail[nw]][i]; 114 q.push(nxt[nw][i]); 115 } 116 } 117 for(int i=1;i<sz;i++){ 118 int u=i,v=fail[u]; 119 addedge(v,u); 120 } 121 } 122 123 void dfs(int u){ 124 bit.add(l[u],1); 125 if(en[u]){ 126 for(int i=0;i<q[en[u]].size();i++){ 127 int v=q[en[u]][i].fi;v=dy[v]; 128 int ind=q[en[u]][i].se; 129 ans[ind]=bit.ask(r[v])-bit.ask(l[v]-1); 130 } 131 } 132 for(int i=0;i<26;i++) 133 if(nxt2[u][i]!=-1) dfs(nxt2[u][i]); 134 bit.add(l[u],-1); 135 } 136 } tr; 137 138 inline void dfs(int u){ 139 l[u]=++tim; 140 r[u]=l[u]; 141 for(int i=head[u];i;i=nxt[i]){ 142 int v=to[i]; 143 dfs(v); 144 r[u]=r[v]; 145 } 146 } 147 148 int main(){ 149 // freopen("2434.in","r",stdin); 150 // freopen("2434.out","w",stdout); 151 tr.init(); 152 tr.doit(); 153 tr.build(); 154 int m=read(); 155 for(int i=1;i<=m;i++){ 156 int x=read(),y=read(); 157 q[y].pb(mp(x,i)); 158 } 159 dfs(0); 160 tr.dfs(0); 161 for(int i=1;i<=m;i++) printf("%d\n",ans[i]); 162 return 0; 163 } 164 165 /* 166 aPaPBbP 167 3 168 1 2 169 1 3 170 2 3 171 */
Review:
-
卡了几个月的问题:输入超时
我们不能维护一个字符串表示当前打字机内的字符串,然后每次暴力插入,这样就超时了
我们应该维护一个当前在trie上的节点,然后来回移动
-
怎么想:
其实不难想
首先肯定上AC自动机,然后询问肯定得离线,那么就是求子树内的和,用dfs序转化成序列上的和是常规套路了