bzoj4231: 回忆树

一道好题。

考虑拆分询问,对于经过LCA的出现位置,可以把前链和后链接近LCA部分的slen-1个字符直接取出进行KMP计算,复杂度O(sigema S)

那么现在就要计算树上一条上至下的链形成的串中,询问串出现了多少次(前链是下至上的,把询问串反过来即可)

考虑把这样的询问拆分成两条根到点的链,离线询问,将询问串建成AC自动机。

最后遍历整棵树同时AC自动机识别,搜到对应位置+1,回溯-1,保证记录的是根到链这个串被AC机识别的情况

当树上遍历到询问结束点,AC机上对应询问串结束点fail树的权值和即为答案,用树状数组维护即可

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const int _=1e2;
const int maxn=1e5+_;
const int maxs=3e5+_;

namespace T
{
    struct node
    {
        int x,y,c,next;
    }a[2*maxn];int len,last[maxn];
    void ins(int x,int y,int c)
    {
        len++;
        a[len].x=x;a[len].y=y;a[len].c=c;
        a[len].next=last[x];last[x]=len;
    }
    int f[30][maxn],dep[maxn];
    int LCA(int x,int y)
    {
        if(dep[x]<dep[y])swap(x,y);
        for(int i=25;i>=0;i--)
            if(dep[x]-dep[y]>=(1<<i))x=f[i][x];
        if(x==y)return x;
        for(int i=25;i>=0;i--)
            if(dep[x]>=(1<<i)&&f[i][x]!=f[i][y])x=f[i][x],y=f[i][y];
        return f[0][x];
    }
    int findkfa(int x,int k)
    {
        for(int i=25;i>=0;i--)
            if(k>=(1<<i))x=f[i][x],k-=(1<<i);
        return x;
    }
    int c[maxn];
    void dfs(int x)
    {
        for(int i=1;(1<<i)<=dep[x];i++)f[i][x]=f[i-1][f[i-1][x]];
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(y!=f[0][x])
            {
                f[0][y]=x;
                dep[y]=dep[x]+1;
                c[y]=a[k].c;
                dfs(y);
            }
        }
    }
    vector<int>vec[maxn];int pos[2*maxn];
    void hang(int x,int z,int slen,int id)
    {
        if(dep[x]-dep[z]<slen)return ;
        int tx=findkfa(x,dep[x]-dep[z]-(slen-1));
        vec[tx].push_back(-id);
        vec[x].push_back(id);
    }
}using namespace T;

char ss[maxs];int slen,as[maxn];
namespace S1
{
    int sa[2*maxn],alen,p[2*maxn];
    int main(int x,int y,int z)
    {
        if(dep[x]+dep[y]-2*dep[z]<slen)return 0;
        alen=0;
        int tx=findkfa(x,max(0,dep[x]-dep[z]-(slen-1)));
        while(tx!=z)sa[++alen]=c[tx],tx=f[0][tx];
        int tip=alen;
        int ty=findkfa(y,max(0,dep[y]-dep[z]-(slen-1)));
        while(ty!=z)sa[++alen]=c[ty],ty=f[0][ty];
        reverse(sa+tip+1,sa+alen+1);
        
        p[1]=0; int j=0;
        for(int i=2;i<=slen;i++)
        {
            while(j!=0&&ss[i]!=ss[j+1])j=p[j];
            if(ss[i]==ss[j+1])j++;
            p[i]=j;
        }
        j=0; int ret=0;
        for(int i=1;i<=alen;i++)
        {
            while(j!=0&&sa[i]!=ss[j+1]-'a'+1)j=p[j];
            if(sa[i]==ss[j+1]-'a'+1)j++;
            if(j==slen)ret++;
        }
        return ret;
    }
}
namespace S2
{
    namespace A
    {
        struct node
        {
            int w[30],fail;
        }tr[2*maxs];int trlen;
        void insert(int id)
        {
            int now=0;
            for(int i=1;i<=slen;i++)
            {
                int x=ss[i]-'a'+1;
                if(tr[now].w[x]==0)tr[now].w[x]=++trlen;
                now=tr[now].w[x];
            }
            T::pos[id]=now;
        }
        int head,tail,list[2*maxs];
        void bfs()
        {
            head=1,tail=2,list[1]=0;
            while(head!=tail)
            {
                int now=list[head]; head++;
                for(int x=1;x<=26;x++)
                {
                    int son=tr[now].w[x]; if(son==0)continue;
                    if(now==0)tr[son].fail=0;
                    else
                    {
                        int pre=tr[now].fail;
                        while(pre!=0&&tr[pre].w[x]==0)pre=tr[pre].fail;
                        tr[son].fail=tr[pre].w[x];
                    }
                    list[tail++]=son;
                }
            }
        }
    }using namespace A;
    namespace F
    {
        struct node
        {
            int x,y,next;
        }a[4*maxs];int len,last[2*maxs];
        void ins(int x,int y)
        {
            len++;
            a[len].x=x;a[len].y=y;
            a[len].next=last[x];last[x]=len;
        }
        int z,L[2*maxs],R[2*maxs];
        void dfs(int x)
        {
            L[x]=++z;
            for(int k=last[x];k;k=a[k].next)
                dfs(a[k].y);
            R[x]=z;
        }
        int s[2*maxs];
        int lowbit(int x){return x&-x;}
        void change(int x,int k){while(x<=trlen+10)s[x]+=k,x+=lowbit(x);}
        int getsum(int x){int ret=0;while(x>0)ret+=s[x],x-=lowbit(x);return ret;}
        void update(int x)
        {
            for(int i=0;i<T::vec[x].size();i++)
            {
                int p=vec[x][i],op=1;
                if(p<0)op=-1,p=-p;
                as[p/2]+=op*(getsum(R[pos[p]])-getsum(L[pos[p]]-1));
            }
        }
    }
    void dfs(int now,int x)
    {
        F::change(F::L[now],1);
        F::update(x);
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(y!=T::f[0][x])
            {
                int tt=now;
                while(tt!=0&&tr[tt].w[T::c[y]]==0)tt=tr[tt].fail;
                dfs(tr[tt].w[T::c[y]],y);
            }
        }
        F::change(F::L[now],-1);
    }
    void main()
    {
        bfs();
        for(int i=1;i<=trlen;i++)F::ins(tr[i].fail,i);
        F::dfs(0);
        dfs(0,1);
    }
}

int main()
{
    int n,Q,x,y; char sc[3];
    scanf("%d%d",&n,&Q);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%s",&x,&y,sc+1);
        ins(x,y,sc[1]-'a'+1),ins(y,x,sc[1]-'a'+1);
    }
    T::dfs(1);
    
    for(int i=1;i<=Q;i++)
    {
        scanf("%d%d%s",&x,&y,ss+1); slen=strlen(ss+1);
        int z=T::LCA(x,y);
        if(y==z)swap(x,y),reverse(ss+1,ss+slen+1);
        if(x==z)
            T::hang(y,x,slen,i*2),S2::insert(i*2);
        else
        {
            as[i]+=S1::main(x,y,z);
            T::hang(y,z,slen,i*2),S2::insert(i*2);
            reverse(ss+1,ss+slen+1);
            T::hang(x,z,slen,i*2+1),S2::insert(i*2+1);
        }
    }
    S2::main();
    
    for(int i=1;i<=Q;i++)printf("%d\n",as[i]);
    
    return 0;
}

 

posted @ 2019-08-03 10:49  AKCqhzdy  阅读(325)  评论(0编辑  收藏  举报