CF547E Mike and Friends 题解
首先,我们可以一眼秒掉AC自动机,一个点的答案就是他以fail为边,子树包含\([l,r]\)的个数的和。然后有人就树套树启动了,我不说是谁。我们发现如果要在线做的话\(O(\log_2^2{n})\)去不掉,最主要的是空间会爆炸,所以我们考虑离线,把询问改为\([1,r]\)的答案减去\([1,l-1]\)的答案,然后暴力加入新的节点,运用线段树/树状数组就可以简单的求出来子树和了。(dfn序列的简单应用),这告诉了我们在时空复杂度搞得时候可以适当的降低要求,比如把在线变成离线,少一些限制则会更容易求解了。
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=5e5+10;
int n,m,jie[maxn],now,tot,ch[maxn][26],ans[maxn],sz[maxn],fail[maxn],fa[maxn],x,y,z,C[maxn],cnt,in[maxn],out[maxn];
string s;
vector<pair<int,int> >qq[maxn];
vector<int>tu[maxn];
int insert(string q){
now=0;
for(int i=0;i<q.size();i++){
if(!ch[now][q[i]-'a']){
tot++;
fa[tot]=now;
ch[now][q[i]-'a']=tot;
}
now=ch[now][q[i]-'a'];
}
return now;
}
void getfail(){
queue<int>Q;
for(int i=0;i<26;i++){
if(ch[0][i]){
Q.push(ch[0][i]);
}
}
while(!Q.empty()){
int nn=Q.front();
Q.pop();
for(int i=0;i<26;i++){
if(ch[nn][i]){
fail[ch[nn][i]]=ch[fail[nn]][i];
Q.push(ch[nn][i]);
}
else{
ch[nn][i]=ch[fail[nn]][i];
}
}
tu[fail[nn]].push_back(nn);
}
return;
}
void dfs(int q){
cnt++;
in[q]=cnt;
for(int i=0;i<tu[q].size();i++){
dfs(tu[q][i]);
}
out[q]=cnt;
return;
}
int lowbit(int q){
return q&(-q);
}
void add(int q,int w){
for(int i=q;i<=cnt;i+=lowbit(i)){
C[i]++;
}
return;
}
int query(int q){
int he=0;
for(int i=q;i;i-=lowbit(i)){
he+=C[i];
}
return he;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>s;
jie[i]=insert(s);
}
getfail();
for(int i=1;i<=m;i++){
cin>>x>>y>>z;
qq[y].push_back({i,z});
if(x>1)qq[x-1].push_back({-i,z});
}
dfs(0);
for(int i=1;i<=n;i++){
now=jie[i];
while(now){
add(in[now],1);
now=fa[now];
}
for(int j=0;j<qq[i].size();j++){
if(qq[i][j].first>0){
ans[qq[i][j].first]+=query(out[jie[qq[i][j].second]])-query(in[jie[qq[i][j].second]]-1);
}
else{
ans[-qq[i][j].first]-=query(out[jie[qq[i][j].second]])-query(in[jie[qq[i][j].second]]-1);
}
}
}
for(int i=1;i<=m;i++){
cout<<ans[i]<<'\n';
}
return 0;
}
浙公网安备 33010602011771号