peiwenjun's blog 没有知识的荒原

CF1098F Ж-function 题解

题目描述

给定长为 \(n\) 的字符串 \(s\)\(q\) 次询问,给定 \(l,r\) ,求:

\[\sum_{i=l}^r\texttt{lcp}(s_{l,r},s_{i,r}) \]

数据范围

  • \(1\le n,q\le 2\cdot 10^5\)

时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{512MB}\)

分析

看不懂树链剖分 & \(\text{DAG}\) 链剖分 & 全局平衡二叉树做法,快来学习好懂又好调的点分治做法。

定义 \(\texttt{lcp(i,j)}\) 表示\(i\) 开始的后缀\(j\) 开始的后缀的最长公共前缀,问题转化为:

\[\sum_{i=l}^r\min(\texttt{lcp(l,i)},r-i+1) \]

根据后缀树理论,两个后缀的 \(\texttt{lcp}\) 等于它们在后缀树上 \(\texttt{lca}\) 节点的最长串长度,问题转化为:

\[\sum_{i=l}^r\min\big(\texttt{len(lca(l,i)},r-i+1\big) \]

考虑点分治,对于单组询问 \((l,r)\) ,我们在点分树\(\texttt{lca(l,i)}\) 处统计 \(i\) 的贡献。

枚举 \(x\)点分树\(l\) 的祖先,讨论原树(后缀树)上 \(l,i,x\) 的位置关系。

注意 \(\texttt{lca}(l,i)=x\) 意味着删掉 \(x\) 后, \(l\)\(i\) 属于不同连通块。

  • \(l\in\texttt{subtree(x)},i\in\texttt{subtree(x)}\)

    根据点分树 \(\texttt{lca(l,i)}=x\) 的限制,原树上 \(\texttt{lca(l,i)}\) 也为 \(x\)

    统计 \(\sum_{i=l}^r\min(\texttt{len(x)},r-i+1)\) 是容易的,用 vector 存储 \(x\) 子树内的 \(i\) ,查询直接二分。

    注意我们还需要保证 \(l,i\)原树上 \(x\) 的不同子树中,利用容斥的思想,对 \(x\) 的每棵子树做一遍上述操作,即可求出多算的贡献。

  • \(l\notin\texttt{subtree(x)},i\in\texttt{subtree(x)}\)

    \(y=\texttt{lca(l,x)}\) ,则原树上 \(\texttt{lca(l,i)=y}\)

    统计 \(\sum_{i=l}^r\min(\texttt{len(y)},r-i+1)\) 的做法和上一种情况完全相同。

  • \(l\in\texttt{subtree(x)},i\notin\texttt{subtree(x)}\)

    此时 \(\texttt{lca(l,i)}=\texttt{lca(x,i)}\) ,对 \(\min\) 取了哪边继续分类讨论。

    \(w=\texttt{len(lca(x,i))}\) ,注意到 \(w\le r-i+1\iff i+w-1\le r\) ,再加上 \(l\le i\le r\) 的限制,这是一个二维数点的问题。

    具体的,我们需要计算 \(\sum\limits_{l\le i\le r,i+w-1\le r}w+\sum\limits_{l\le i\le r,i+w-1>r}(r-i+1)\) ,分别用树状数组维护即可。

  • \(l\not\in\texttt{subtree(x)},i\not\in\texttt{subtree(x)}\)

    删掉 \(x\) 并不能导致 \(l,i\) 不连通,换言之这种情况不存在

时间复杂度 \(\mathcal O((n+q)\log^2n)\)

实现细节:

  • 注意第 \(i\) 个后缀在 fail 树上的节点并不是 i ,而是插入后缀 \(s_{i,n}\) 到达的节点。

    因此我们需要对后缀树的节点重标号,使得 \(s_{i,n}\) 对应节点 i

  • 由于询问可以离线,我们不需要显示建出点分树,而是在点分治的过程中 dfs 当前连通块,处理连通块中每个点挂着的所有询问。

    我们还需要计算连通块内每个点与分治中心 \(x\)\(\texttt{lca}\) ,可以预处理每个点的深度, dfs 过程中将访问过的最浅点作为参数传入。

  • 初始总点数为后缀树节点个数 tot 而不是 n不要像我一样把总点数传错了然后一直在想为啥会被卡常

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,ll>
using namespace std;
const int maxn=4e5+5;
int l,m,n,r,rt,all,lst=1,tot=1;
char s[maxn];
int id[maxn],len[maxn];
int bel[maxn],dep[maxn],pos[maxn];
int mx[maxn],sz[maxn];
bool vis[maxn];
ll res[maxn];
vector<int> now,g[maxn];
vector<pii> h[maxn],q[maxn];
struct quer
{
    int x,r,id,sgn;
};
inline bool cmp(const quer &a,const quer &b)
{
    return a.x<b.x;
}
struct bit1
{
    ll c[maxn];
    inline void add(int x,int v)
    {
        while(x) c[x]+=v,x-=x&(-x);
    }
    inline ll query(int x)
    {
        ll res=0;
        while(x<=n) res+=c[x],x+=x&(-x);
        return res;
    }
}t1,t2;
struct bit2
{
    ll c[maxn];
    inline void add(int x,int v)
    {
        while(x<=n) c[x]+=v,x+=x&(-x);
    }
    inline ll query(int x)
    {
        ll res=0;
        while(x) res+=c[x],x-=x&(-x);
        return res;
    }
}t3;
struct node
{
    int ch[26];
    int fa,len;
}f[maxn];
void insert(int c)
{
    int p=lst,np=lst=++tot;
    f[np].len=f[p].len+1;
    while(p&&!f[p].ch[c]) f[p].ch[c]=np,p=f[p].fa;
    if(!p) f[np].fa=1;
    else
    {
        int q=f[p].ch[c];
        if(f[q].len==f[p].len+1) f[np].fa=q;
        else
        {
            int nq=++tot;
            f[nq]=f[q],f[nq].len=f[p].len+1,f[q].fa=f[np].fa=nq;
            while(p&&f[p].ch[c]==q) f[p].ch[c]=nq,p=f[p].fa;
        }
    }
}
void addedge(int u,int v)
{
    g[u].push_back(v),g[v].push_back(u);
}
void dfs1(int u,int fa)
{
    for(auto v:g[u]) if(v!=fa) dep[v]=dep[u]+1,dfs1(v,u);
}
void dfs2(int u,int fa,int x,int p)
{
    if(dep[u]<dep[p]) p=u;
    bel[u]=x,pos[u]=p,now.push_back(u);
    for(auto v:g[u]) if(!vis[v]&&v!=fa) dfs2(v,u,x,p);
}
inline ll ask1(const vector<pii> &vec,int x,int l,int r)
{///\sum_{i\in vec,l\le i\le r}\min(x,r-i+1)
    auto get=[&](int x)
    {
        return x>=0?vec[x].se:0;
    };
    auto ask2=[&](int x,int y,int r)
    {///\sum_{i\in vec,i\le r}\min(x,y-i)
        int a=lower_bound(vec.begin(),vec.end(),mp(r+1,0ll))-vec.begin();
        if(r<=y-x) return 1ll*a*x;
        int b=lower_bound(vec.begin(),vec.end(),mp(y-x,0ll))-vec.begin();
        return 1ll*b*x+1ll*(a-b)*y-(get(a-1)-get(b-1));
    };
    return ask2(x,r+1,r)-ask2(x,r+1,l-1);
}
void calc(int u)
{
    bel[u]=pos[u]=u,now={u};
    for(auto v:g[u]) if(!vis[v]) dfs2(v,u,v,u);
    for(auto v:now) h[bel[v]].push_back(mp(v,v));
    vector<pii> vec;
    for(auto v:now) if(pos[v]==u) vec.push_back(mp(v,v));
    sort(vec.begin(),vec.end());
    for(int i=1;i<vec.size();i++) vec[i].se+=vec[i-1].se;
    for(auto v:g[u])
    {
        if(vis[v]) continue;
        sort(h[v].begin(),h[v].end());
        for(int i=1;i<h[v].size();i++) h[v][i].se+=h[v][i-1].se;
    }
    vector<quer> cur;
    for(auto i:now)
    {
        for(auto p:q[i])
        {
            int l=i,r=p.fi,id=p.se;
            ///l\in subtree(x),i\in subtree(x)
            if(pos[i]==u)
            {
                res[id]+=ask1(vec,len[u],l,r);
                if(i!=u) res[id]-=ask1(h[bel[i]],len[u],l,r);
            }
            ///l\not\in subtree(x),i\in subtree(x)
            else res[id]+=ask1(vec,len[pos[i]],l,r);
            ///l\in subtree(x),i\not\in subtree(x)
            if(pos[i]==u) cur.push_back({l-1,r,id,-1}),cur.push_back({r,r,id,1});
        }
    }
    ///二维数点
    sort(now.begin(),now.end());
    sort(cur.begin(),cur.end(),cmp);
    int j=0;
    for(auto p:cur)
    {
        while(j<now.size()&&now[j]<=p.x)
        {
            int i=now[j++],w=len[pos[i]];
            if(pos[i]==u||!w) continue;
            t1.add(i+w-1,1),t2.add(i+w-1,i),t3.add(i+w-1,w);
        }
        int r=p.r;
        res[p.id]+=(t1.query(r+1)*(r+1ll)-t2.query(r+1)+t3.query(r))*p.sgn;
    }
    ///清空
    while(j)
    {
        int i=now[--j],w=len[pos[i]];
        if(pos[i]==u||!w) continue;
        t1.add(i+w-1,1),t2.add(i+w-1,i),t3.add(i+w-1,w);
    }
    for(auto v:g[u]) if(!vis[v]) h[v].clear();
}
void getroot(int u,int fa)
{
    sz[u]=1,mx[u]=0;
    for(auto v:g[u])
    {
        if(vis[v]||v==fa) continue;
        getroot(v,u);
        sz[u]+=sz[v],mx[u]=max(mx[u],sz[v]);
    }
    mx[u]=max(mx[u],all-sz[u]);
    if(!rt||mx[u]<mx[rt]) rt=u;
}
void solve(int u)
{
    vis[u]=true,calc(u);
    for(auto v:g[u])
    {
        if(vis[v]) continue;
        all=sz[v],getroot(v,rt=0),solve(rt);
    }
}
int main()
{
    scanf("%s%d",s+1,&m),n=strlen(s+1);
    for(int i=1;i<=m;i++) scanf("%d%d",&l,&r),q[l].push_back(mp(r,i));
    for(int i=n;i>=1;i--) insert(s[i]-'a'),id[lst]=i;
    for(int i=1,num=n;i<=tot;i++) if(!id[i]) id[i]=++num;
    for(int i=1;i<=tot;i++) len[id[i]]=f[i].len;
    for(int i=2;i<=tot;i++) addedge(id[f[i].fa],id[i]);
    dep[id[1]]=1,dfs1(id[1],0);
    all=tot,getroot(1,0),solve(rt);
    for(int i=1;i<=m;i++) printf("%lld\n",res[i]);
    return 0;
}

posted on 2023-06-17 17:31  peiwenjun  阅读(8)  评论(0)    收藏  举报

导航