CF1073G Yet Another LCP Problem

一、题目

点此看题

二、解法

先对原串的反串建后缀自动机,然后两个后缀的 l c p lcp lcp就是它们在自动机上的 l c a lca lca长度。

然后就可以在后缀自动机 d p dp dp,设 s a [ u ] sa[u] sa[u] u u u子树内 A A A集合元素个数, s b [ u ] sb[u] sb[u]意义差不多,那么答案就可以这样算:
( s a [ u ] − s a [ v ] ) ∗ s b [ v ] ∗ l e n [ u ] (sa[u]-sa[v])*sb[v]*len[u] (sa[u]sa[v])sb[v]len[u]注意此时 s a , s b sa,sb sa,sb没把本节点是不是集合元素算进去,所以还要继续算贡献:
( a [ u ] ∗ b [ u ] + a [ u ] ∗ s b [ u ] + b [ u ] ∗ s a [ u ] ) ∗ l e n [ u ] (a[u]*b[u]+a[u]*sb[u]+b[u]*sa[u])*len[u] (a[u]b[u]+a[u]sb[u]+b[u]sa[u])len[u]然后这道题给的询问很多,但是关键点总数是确定的,显然是虚树优化 d p dp dp

思路到此就讲完了,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn),注意虚树的数组不能开小(我调了一个小时 q w q qwq qwq),然后就是 c m p cmp cmp不要写在结构体里面,更多细节看我的代码吧 q w q qwq qwq

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <stack>
using namespace std;
const int M = 400005;
int read()
{
 int x=0,flag=1;char c;
 while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
 while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
 return x*flag;
}
int n,m,k;char s[M];
int p[M][20],dep[M],dfin[M],dfou[M];
struct edge
{
    int v,next;
    edge(int V=0,int N=0) : v(V) , next(N) {}
};
bool cmp(int x,int y)
{
    int t1=x>0?dfin[x]:dfou[-x];
    int t2=y>0?dfin[y]:dfou[-y];
    return t1<t2;
}
struct automaton
{
    int n,cnt,last,len[M],fa[M],ch[M][26],tr[M];
    int k,k1,A,B,t[M<<1],vis[M],a[M],b[M],sa[M],sb[M];
    edge e[2*M];int Index,tot,f[M];long long ans;
    stack<int> s;vector<int> G[M];
    automaton() {cnt=last=1;}
    void add(int c)
    {
        int p=last,np=last=++cnt;
        len[np]=len[p]+1;
        for(;p && !ch[p][c];p=fa[p]) ch[p][c]=np;
        if(!p) fa[np]=1;
        else
        {
            int q=ch[p][c];
            if(len[q]==len[p]+1) fa[np]=q;
            else
            {
                int nq=++cnt;
                fa[nq]=fa[q];
                memcpy(ch[nq],ch[q],sizeof ch[q]);
                len[nq]=len[p]+1;
                fa[q]=fa[np]=nq;
                for(;p && ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
            }
        }
        tr[++n]=last;
    }
    void dfs1(int u)
    {
        p[u][0]=fa[u];
        dep[u]=dep[fa[u]]+1;
        for(int i=1;i<20;i++)
            p[u][i]=p[p[u][i-1]][i-1];
        dfin[u]=++Index;
        for(int i=f[u];i;i=e[i].next)
            if(e[i].v^fa[u])
                dfs1(e[i].v);
        dfou[u]=++Index;
    }
    void build()
    {
        for(int i=2;i<=cnt;i++)
        {
            e[++tot]=edge(fa[i],f[i]),f[i]=tot;
            e[++tot]=edge(i,f[fa[i]]),f[fa[i]]=tot;
        }
        dfs1(1);
    }
    int get(int u,int v)
    {
        if(dep[u]<=dep[v]) swap(u,v);
        for(int i=19;i>=0;i--)
            if(dep[p[u][i]]>=dep[v])
                u=p[u][i];
        if(u==v) return u;
        for(int i=19;i>=0;i--)
            if(p[u][i]^p[v][i])
                u=p[u][i],v=p[v][i];
        return p[u][0];
    }
    void dfs2(int u,int fa)
    {
        for(int i=0;i<G[u].size();i++)
        {
            int v=G[u][i];
            if(v^fa)
                dfs2(v,u),sa[u]+=sa[v],sb[u]+=sb[v];
        }
        for(int i=0;i<G[u].size();i++)
        {
            int v=G[u][i];
            if(v^fa)
            {
                ans+=1ll*(sa[u]-sa[v])*sb[v]*len[u];
                a[v]=b[v]=sa[v]=sb[v]=vis[v]=0;
                G[v].clear();
            }
        }
        ans+=(1ll*a[u]*b[u]+1ll*a[u]*sb[u]+1ll*b[u]*sa[u])*len[u];
        sa[u]+=a[u];sb[u]+=b[u];
    }
    void solve()
    {
        k=ans=0;
        A=read();B=read();
        for(int i=1;i<=A;i++)
        {
            t[++k]=tr[n-read()+1];
            vis[t[k]]=1;a[t[k]]++;
        }
        for(int i=1;i<=B;i++)
        {
            int x=tr[n-read()+1];
            if(!vis[x]) t[++k]=x,vis[x]=1;
            b[x]++;
        }
        sort(t+1,t+1+k,cmp);
        k1=k;
        for(int i=1;i<k1;i++)
        {
            int tmp=get(t[i],t[i+1]);
            if(!vis[tmp]) t[++k]=tmp,vis[tmp]=1;
        }
        if(!vis[1]) t[++k]=1,vis[1]=1;
        k1=k;
        for(int i=1;i<=k1;i++)
            t[++k]=-t[i];
        sort(t+1,t+1+k,cmp);
        for(int i=1;i<=k;i++)
        {
            if(t[i]>0) s.push(t[i]);
            else
            {
                int x=s.top();s.pop();
                if(x==1) break;
                G[x].push_back(s.top());
                G[s.top()].push_back(x);
            }
        }
        dfs2(1,0);
        printf("%lld\n",ans);
        vis[1]=sa[1]=sb[1]=a[1]=b[1]=0;
        G[1].clear();
    }
}Sam;
int main()
{
    n=read();m=read();
    scanf("%s",s);
    for(int i=n-1;i>=0;i--)
        Sam.add(s[i]-'a');
    Sam.build();
    while(m--)
        Sam.solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值