Codeforces, 587F: Duff is Mad

Duff is Mad

给出 n 个字符串S1,S2......Sn q 个询问。
每次询问给定l,r,k, 要求输出 ri=loccur(Si,Sk) ,.
其中 occur(S,T) 表示 S T中出现的次数。
n,q,ni=1|Si|105

Solution

  • 考虑建立AC自动机, fail 指针形成了一棵 par 树。
  • End(i) 表示第 i 个字符串对应的结束结点。对于询问(l,r,k) 其实就是把从 root End(i) 上的所有节点以及他们在 par 树 上的祖先结点里面 SlSr 的结束结点的个数。
  • 对于 length(x)>n 的字符串 Sx ,单独DFS一遍,求出 cnt(i) 表示第 End(i) 出现了几次,求 cnt 的前缀和就可以 O(1) 回答 k=x 的询问。
  • 对于 length(x)n 的字符串 Sx ,在 root End(x) 上的所有节点的相关结点集合中加入 x <script type="math/tex" id="MathJax-Element-3863">x</script>.DFS整棵树,给所有相关集合中的点的询问加上贡献。
  • 需要动态维护前缀和。注意到询问次数比较多,用分块维护。

Code

#include<cstdio>  
#include<iostream>  
#include<cstdlib>  
#include<cstring>  
#include<cmath>
#include<vector>
#include<queue>

#define LL long long
#define CLEAR(xxx) memset(xxx,0,sizeof(xxx)) 

using namespace std;  
const int maxn=150000; 

char s[maxn];
int n,qq,tot=1,lim;
int Root=1,len[maxn],End[maxn];
vector<int> word[maxn];
vector<int> sons[maxn];
vector<int> A[maxn];
LL ans[maxn];
struct node{
    int Next[26],fa,fail;
}T[maxn];
struct Ask{
    int L,R,id;
    Ask(){}
    Ask(int L,int R,int id):L(L),R(R),id(id){}
};
vector<Ask> Q[maxn];

queue<int> q;
void Build_AC_Automaton(){
    q.push(Root);
    int i,j;
    while(!q.empty()){
        int x= q.front(); q.pop();
        for(i=0;i<26;i++)
            if(T[x].Next[i]){
                int v= T[x].Next[i];
                for(j=T[x].fail;j && !T[j].Next[i]; j=T[j].fail);
                T[v].fail= j? T[j].Next[i]:Root;
                q.push(v);
            }
    }
}

LL sum[maxn],f[maxn];

void dfs(int x){
    int i;
    for(i=0;i<sons[x].size();i++){
        dfs(sons[x][i]);
        f[x]+=f[sons[x][i]];
    }
    for(i=0;i<word[x].size();i++)
        sum[word[x][i]]+=f[x];
}

void SolveLongWords(){
    int i,j;
    for(i=1;i<=n;i++)
        if(len[i]>lim){
            CLEAR(sum); CLEAR(f);
            for(j=End[i];j;j=T[j].fa) f[j]=1;
            dfs(1);
            for(j=1;j<=n;j++) sum[j]+=sum[j-1];
            for(j=0;j<Q[i].size();j++)
                ans[Q[i][j].id] = sum[Q[i][j].R]-sum[Q[i][j].L-1];
        }
}

LL Sum[maxn],delta[maxn];
int id[maxn];

void Add(int x,int d){
    for(int i=x;id[i]==id[x];i++) Sum[i]+=d;
    for(int i=id[x]+1;i<=id[n];i++) delta[i]+=d;
}
LL Query(int x){
    return Sum[x]+delta[id[x]];
}

void DFS(int x){
    int i,j;
    for(i=0;i<word[x].size();i++) Add(word[x][i],1);
    for(i=0;i<A[x].size();i++){
        int o = A[x][i];
        for(j=0;j<Q[o].size();j++){
            Ask t= Q[o][j];
            ans[t.id]+=Query(t.R)-Query(t.L-1);
        }
    }
    for(i=0;i<sons[x].size();i++) DFS(sons[x][i]);
    for(i=0;i<word[x].size();i++) Add(word[x][i],-1);
}

int main(){
    int i,j,k,x,y;
    scanf("%d%d",&n,&qq);
    lim= (int)sqrt(n);
    for(i=1;i<=n;i++) id[i]=(i-1)/lim+1; 
    for(i=1;i<=n;i++){
        scanf("%s",s+1);
        len[i]= strlen(s+1);
        int cur= Root;
        for(j=1;j<=len[i];j++){
            int c= s[j]-'a';
            if(!T[cur].Next[c])
                T[cur].Next[c]= ++tot,T[tot].fa=cur;
            cur= T[cur].Next[c];
            if(len[i]<=lim) A[cur].push_back(i);
        }
        End[i] = cur;
        word[cur].push_back(i); 
    }
    Build_AC_Automaton();
    for(i=1;i<=tot;i++) sons[T[i].fail].push_back(i);  //建 par 树 
    for(i=1;i<=qq;i++){
        scanf("%d%d%d",&x,&y,&k);
        Q[k].push_back(Ask(x,y,i));
    }
    SolveLongWords();
    DFS(1);
    for(i=1;i<=qq;i++)
        printf("%I64d\n",ans[i]); 
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值