CF1098F Ж-function 题解
题目描述
给定长为 \(n\) 的字符串 \(s\) 。 \(q\) 次询问,给定 \(l,r\) ,求:
数据范围
- \(1\le n,q\le 2\cdot 10^5\) 。
时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{512MB}\) 。
分析
看不懂树链剖分 & \(\text{DAG}\) 链剖分 & 全局平衡二叉树做法,快来学习好懂又好调的点分治做法。
定义 \(\texttt{lcp(i,j)}\) 表示从 \(i\) 开始的后缀和从 \(j\) 开始的后缀的最长公共前缀,问题转化为:
根据后缀树理论,两个后缀的 \(\texttt{lcp}\) 等于它们在后缀树上 \(\texttt{lca}\) 节点的最长串长度,问题转化为:
考虑点分治,对于单组询问 \((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;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17487772.html
浙公网安备 33010602011771号