Codeforces Round #326 (Div. 1) F. Duff is Mad

题意

自己去看。。

题解

考虑AC自动机
然后分块
如果被询问串的长度是大于 n n 的,那么就对于每一个串一起错,每一次遍历整一颗树,统计一个前缀和,就可以做到 O(1) O ( 1 ) 回答了
否则,问题就就先变成r的答案减去l-1的答案
然后这样的话,就按照前缀排序就可以了
dfs一下,然后每一次就相当于子树加
我比较懒。。于是写了树状数组
复杂度是 nnlogn n n l o g n
实测,会被卡
怎么优化呢?
考虑每一次询问是 nlogn n l o g n 的,但是修改是 logn l o g n
平衡复杂度,用分块来代替树状数组
就可以做到,询问 n n ,修改也是 n n 了,可以通过
最近要做的题比较多,就先不打了

放一个带log的做法

CODE:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
typedef long long LL;
const LL mx=200;
const LL N=100005*2;
LL n,q;
string ss[N];
struct qt
{
    LL fail;
    LL son[26];
}tr[N];LL nn=0;
struct qy
{
    LL x,y,last;
}e[N];LL num,last[N];
LL pos[N];
LL len[N];
void init (LL x,LL y)
{
    num++;
    e[num].x=x;e[num].y=y;
    e[num].last=last[x];
    last[x]=num;
}
void Ins (LL x)
{
    LL now=0;
    len[x]=ss[x].size();
    for (LL u=0;u<len[x];u++)
    {
        LL xx=ss[x][u]-'a';
        if (tr[now].son[xx]==0) tr[now].son[xx]=++nn;
        now=tr[now].son[xx];
    }
    pos[x]=now;
}
void bfs ()
{
    queue<int> q;
    q.push(0);
    while (!q.empty())
    {
        LL x=q.front();q.pop();
        for (LL u=0;u<26;u++)
        {
            LL son=tr[x].son[u],f=tr[x].fail;
            if (son>0)
            {
                if (x==0)   tr[son].fail=0;
                else tr[son].fail=tr[f].son[u];
                q.push(son);
            }
            else if (x!=0) tr[x].son[u]=tr[f].son[u];
        }
    }
    for (LL u=1;u<=nn;u++)  init(tr[u].fail,u);
}
struct qq
{
    LL l,r,q,id;
}A[N],B[N];//小大
LL L[N],R[N],cnt;
LL tot,tot1;
LL ans[N];
bool cmp1 (qq x,qq y)   {return x.q<y.q;}
LL o[N];
LL sum[N];
void bfs1 (LL x)
{
    memset(sum,0,sizeof(sum));
    LL now=0;
    for (LL u=0;u<len[x];u++)
    {
        LL xx=ss[x][u]-'a';
        now=tr[now].son[xx];
        LL now1=now;
        while (now1!=0) {sum[now1]++;now1=tr[now1].fail;}
    }
}
void solveB ()
{
    sort(B+1,B+1+tot1,cmp1);
    LL now=0;
    for (LL u=1;u<=tot1;u++)
    {
        if (now!=B[u].q)
        {
            now=B[u].q;
            bfs1(now);
            for (LL u=1;u<=n;u++)   o[u]=o[u-1]+sum[pos[u]];
        }
        ans[B[u].id]=o[B[u].r]-o[B[u].l-1];
    }
}
bool cmpA (qq a,qq b){return a.l<b.l;}
LL f[N];
LL lb (LL x){return x&(-x);}
LL get (LL x)
{
    LL lalal=0;
    while (x>=1)    {lalal=lalal+f[x];x-=lb(x);}
    return lalal;
}
void add (LL x,LL y)    {while (x<=cnt) {f[x]+=y;x+=lb(x);}}
void solveA ()
{
    sort(A+1,A+1+tot,cmpA);
    LL now=0;
    for (LL u=1;u<=tot;u++)
    {
        while (now<A[u].l)  {now++;add(L[pos[now]],1);add(R[pos[now]]+1,-1);}
        LL x=0;
        LL h=A[u].q;
        for (LL i=0;i<len[h];i++)
        {
            x=tr[x].son[ss[h][i]-'a'];
            //printf("TKJ:%I64d %I64d %I64d\n",L[x],x,get(L[x]));
            ans[A[u].id]=ans[A[u].id]+A[u].r*get(L[x]);
        }
    }
}
void dfs (LL x)
{
    L[x]=++cnt;
    for (LL u=last[x];u!=-1;u=e[u].last)
    {
        LL y=e[u].y;
        dfs(y);
    }
    R[x]=cnt;
}
int main()
{
//  freopen("a.in","r",stdin);
    num=0;memset(last,-1,sizeof(last));
    scanf("%I64d%I64d",&n,&q);
    for (LL u=1;u<=n;u++)
    {
        cin>>ss[u];
        Ins(u);
        //printf("OZY:%I64d\n",pos[u]);
    }

    bfs();
    for (LL u=1;u<=q;u++)
    {
        LL l,r,q;
        scanf("%I64d%I64d%I64d",&l,&r,&q);
        if (len[q]>=mx) {tot1++;B[tot1].l=l;B[tot1].r=r;B[tot1].id=u;B[tot1].q=q;}
        else    
        {
            if (l>1)    {tot++;A[tot].l=l-1;A[tot].r=-1;A[tot].id=u;A[tot].q=q;}
            tot++;A[tot].l=r;A[tot].r=1;A[tot].id=u;A[tot].q=q;
        }
    }
    dfs(0);
    solveA();
    solveB();
    for (LL u=1;u<=q;u++)   printf("%I64d\n",ans[u]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值