51nod 1440 迈克打电话

题意

有n只熊,从1到n进行编号。
第i只熊的电话号码是si。每只熊会给那些电话号码是他的子串的熊打电话(可能会给自己打)。
call(i, j) 表示第i只熊给第j只熊打电话的次数,也就是第j个串在第i个串中出现的次数。
迈克会有q次询问。每个询问中给出l,r,k,然后请您计算一下 ∑ri=lcall(i,k)

题解

这题想了一会就想出来了,感觉不难啊
这种东西考虑AC自动机就好了啊
建立fail树
每个节点可能有若干个值
然后问题转化为了询问一个子树里面,l~r里面所有数出现次数的中和
这个的话,我自认为想到了一个比较优秀的做法
拆成两个,答案是 1 r 1   r 的答案- 1 (l1) 1   ( l − 1 ) 的答案
这个的话,分别按照r和l排序,做两次
每一次假如这个点,就暴力吧他所有链的状态++,这个的代价是长度总和
然后dfs序询问子树就可以了
时间复杂度 O(nlogn+qlogq) O ( n l o g n + q l o g q )
看大家T了这个多发,我这个很稳啊,只是一开始数组开小T了一发
CODE:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
typedef long long LL;
const int N=500005;
int n,q;
vector<char> ss[N];
struct qq
{
    int son[26],fail,c;
}tr[N];int tot=0,now;
int lalal[N];
void Ins (int x)
{
    if (tr[now].son[x]==0)  tr[now].son[x]=++tot;
    now=tr[now].son[x];
}
void get_fail ()
{
    queue<int> q;
    q.push(0);
    while (!q.empty())
    {
        int x=q.front();q.pop();
        for (int u=0;u<26;u++)
        {
            int y=tr[x].son[u],f=tr[x].fail;
            if (y==0) {tr[x].son[u]=tr[f].son[u];continue;}
            if (x==0) tr[y].fail=0;
            else tr[y].fail=tr[f].son[u];
            q.push(y);
        }
    }
}
LL ans[N];
struct qt{int l,r,k,id;}s[N];
struct qy{int x,y,last;}e[N];int num,last[N];
void init (int x,int y)
{
    num++;
    e[num].x=x;e[num].y=y;
    e[num].last=last[x];
    last[x]=num;
}
int L[N],R[N];
bool cmpr (qt x,qt y){return x.r<y.r;}
bool cmpl (qt x,qt y){return x.l<y.l;}
LL f[N];
int lb (int x){return x&(-x);}
void change (int x){while (x<=num){f[x]++;x+=lb(x);}}
int get (int x)
{
    int lalal=0;
    while (x>=1)
    {
        lalal=lalal+f[x];
        x-=lb(x);
    }
    return lalal;
}
void solve1 ()
{
    memset(f,0,sizeof(f));
    sort(s+1,s+1+q,cmpr);
    int o=1;
    for (int u=1;u<=n;u++)
    {
        int len=ss[u].size(),now=0;
        for (int i=0;i<len;i++)
        {
            int x=ss[u][i]-'a';
            now=tr[now].son[x];
            change(L[now]);
        }
        while (o<=q&&s[o].r==u) 
        {
            ans[s[o].id]=ans[s[o].id]+get(R[lalal[s[o].k]])-get(L[lalal[s[o].k]]-1);
            o++;
        }
    }
}
void solve2 ()
{
    memset(f,0,sizeof(f));
    sort(s+1,s+1+q,cmpl);
    int o=1;
    while (o<=q&&s[o].l==0) o++;
    for (int u=1;u<=n;u++)
    {
        int len=ss[u].size(),now=0;
        for (int i=0;i<len;i++)
        {
            int x=ss[u][i]-'a';
            now=tr[now].son[x];
            change(L[now]);
        }
        while (o<=q&&s[o].l==u)
        {
            ans[s[o].id]=ans[s[o].id]-(get(R[lalal[s[o].k]])-get(L[lalal[s[o].k]]-1));
            o++;
        }
    }
}
void dfs (int x)
{
    L[x]=++num;
    for (int u=last[x];u!=-1;u=e[u].last)
    {
        int y=e[u].y;
        dfs(y);
    }
    R[x]=num;
}
int main()
{
    num=0;memset(last,-1,sizeof(last));
    scanf("%d%d",&n,&q);
    for (int u=1;u<=n;u++)
    {
        now=0;
        char ch=getchar();
        while (ch<'a'||ch>'z') ch=getchar();
        while (ch>='a'&&ch<='z') {ss[u].push_back(ch);Ins(ch-'a');ch=getchar();}
        lalal[u]=now;
    }
    get_fail();
    for (int u=1;u<=tot;u++)    init(tr[u].fail,u);
    num=0;dfs(0);
//  for (int u=1;u<=tot;u++) printf("%d %d\n",L[u],R[u]);
    for (int u=1;u<=q;u++)  {scanf("%d%d%d",&s[u].l,&s[u].r,&s[u].k);s[u].id=u;s[u].l--;}
    solve1();solve2();
    for (int u=1;u<=q;u++) printf("%lld\n",ans[u]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值