后缀自动机
首先考虑第一问:
我们先将后缀自动机建出来,考虑每个节点,它出现的次数肯定是endpos的size(我们记为num),那么选取这个节点的串的方案数即为C(num,2)=num*(num-1)/2,所能贡献的长度区间为这个节点对应的所有串的长度即[ len[fa[x]]+1 , len[x]+1 ],这里可以差分一下,最后前缀和就是答案了。
然后考虑第二问:
我们考虑每当我们在后缀自动机上新建一个节点,就把这个位置的a[i]给赋值上去。当然,在建的过程中由于endpos裂开而产生的的那个新的节点不赋值,因为最后因endpos裂开的而产生的新节点的nq在parent tree上一定是原节点q的father,所以最后的值会更新上去,如果赋值了,就相当于额外多了一个值,答案当然会不对。
那么我们再考虑在parent tree上应该怎么做。在parent tree上,father一定是son的后缀,而对于这题来说,某两个串是r相似的,那么它们一定是[0,r-1]相似的,即一对r相似串的a1*a2有贡献那么对它们的前缀一定也有贡献,所以我们可以将串倒序建后缀自动机,这样后缀就变成了前缀,lcp就变成了lca!因为此题a有负数,所以我们要维护最大次大最小次小,那么因为现在parent tree上每个父亲都是儿子的前缀,所以子树的最优可以更新父亲的最优。那么自然的我们就可以对每个节点维护四个值(最大次大最小次小),然后每个节点的值为它即它所有节点的最后(树形dp转移),然后贡献的区间显然为[ len[fa[x]]+1 , len[x]+1 ],这个我们可以用线段树维护一下,这是直接可以标记永久化的,也非常好写。
效率:O(nlogn)
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>
#define pb push_back
#define lch a[n].lc
#define rch a[n].rc
using namespace std;
const long long inf=1e18+10;
const int maxn=6e5+10;
int n;
char s[maxn];
long long vvv[maxn];
struct suda
{
struct da {int ch[30],fa,len;}po[maxn];
vector<long long>now[maxn];
struct daa{int lc,rc;long long tag;}a[4*maxn];
int las,tot,si[maxn],to[maxn],nt[maxn],st[maxn],topt,root,cnt;
long long v[maxn],sum[maxn],ansv[maxn],ma[maxn],cima[maxn],mi[maxn],cimi[maxn];
void init()
{
memset(po,0,sizeof po); memset(si,0,sizeof si);
memset(to,0,sizeof to); memset(nt,0,sizeof nt);
memset(st,0,sizeof st); memset(v,0,sizeof v);
memset(sum,0,sizeof sum);
las=tot=1; topt=0; //po[0].len=-1;
for (int i=1;i<=6e5;i++)
{
now[i].clear(); ansv[i]=-inf;
ma[i]=cima[i]=-inf;
mi[i]=cimi[i]=inf;
}
}
void insert(int x,long long val)
{
int p=las,np=las=++tot; si[np]=1; po[np].len=po[p].len+1;
ma[np]=val; mi[np]=val; //printf("%d addv-> %lld\n",np,val);
for (;p&&(!po[p].ch[x]);p=po[p].fa) po[p].ch[x]=np;
if (!p) po[np].fa=1;
else
{
int q=po[p].ch[x];
if (po[q].len==po[p].len+1) po[np].fa=q;
else
{
int nq=++tot; po[nq]=po[q];
po[nq].len=po[p].len+1;
po[q].fa=po[np].fa=nq;
for (;p&&(po[p].ch[x]==q);p=po[p].fa) po[p].ch[x]=nq;
}
}
}
void add(int x,int y){to[++topt]=y; nt[topt]=st[x]; st[x]=topt;}
void build_tree(int &n,int l,int r)
{
n=++cnt; a[n].tag=-inf;
if (l==r) return ;
int mid=(l+r)>>1;
build_tree(lch,l,mid); build_tree(rch,mid+1,r);
}
void tree_max(int n,int L,int R,int l,int r,long long kk)
{
if (L==l && R==r)
{
a[n].tag=max(a[n].tag,kk);
//if (l==r) printf("kkqqqqq %lld\n",a[n].tag);
return;
}
int mid=(L+R)>>1;
if (r<=mid) tree_max(lch,L,mid,l,r,kk);
else if (l>=mid+1) tree_max(rch,mid+1,R,l,r,kk);
else tree_max(lch,L,mid,l,mid,kk),tree_max(rch,mid+1,R,mid+1,r,kk);
}
void qury(int n,int l,int r,int lc)
{
ansv[lc]=max(ansv[lc],a[n].tag);
if (l==r) return;
int mid=(l+r)>>1;
if (lc<=mid) qury(lch,l,mid,lc);else qury(rch,mid+1,r,lc);
}
void getsi(int x)
{
int p=st[x];
while (p)
{
getsi(to[p]); si[x]+=si[to[p]];
if (ma[to[p]]>=ma[x]) {cima[x]=ma[x]; ma[x]=ma[to[p]];}
else if (ma[to[p]]>cima[x]) cima[x]=ma[to[p]];
if (cima[to[p]]>=ma[x]) {cima[x]=ma[x]; ma[x]=cima[to[p]];}
else if (cima[to[p]]>cima[x]) cima[x]=cima[to[p]];
if (mi[to[p]]<=mi[x]) {cimi[x]=mi[x]; mi[x]=mi[to[p]];}
else if (mi[to[p]]<cimi[x]) cimi[x]=mi[to[p]];
if (cimi[to[p]]<=mi[x]) {cimi[x]=mi[x]; mi[x]=cimi[to[p]];}
else if (cimi[to[p]]<cimi[x]) cimi[x]=cimi[to[p]];
p=nt[p];
}
if (x==1) v[0]+=1ll*si[x]*(si[x]-1)/2ll;
else
{
v[po[po[x].fa].len+1]+=1ll*si[x]*(si[x]-1)/2ll;
v[po[x].len+1]-=1ll*si[x]*(si[x]-1)/2ll;
}
if (si[x]>=2 && x!=1)
{
//printf("kkqq %d %d %lld %lld\n",po[po[x].fa].len+1,po[x].len,ma[x]*cima[x],mi[x]*cimi[x]);
if (ma[x]!=-inf && cima[x]!=-inf)
tree_max(root,1,n,po[po[x].fa].len+1,po[x].len,ma[x]*cima[x]);
if (mi[x]!=inf && cimi[x]!=inf)
tree_max(root,1,n,po[po[x].fa].len+1,po[x].len,mi[x]*cimi[x]);
}
//printf("kkqq %d %d\n",x,si[x]);
//if (si[x]) now[si[x]].pb(po[x].vv),printf("kkqqkkqq %d si[x]=%d %lld\n",x,si[x],po[x].vv);
}
void solve()
{
for (int i=1;i<=tot;i++)
{
add(po[i].fa,i);
//printf("kqkq %d %d\n",po[i].fa,i);
//printf("kqkqk %d len -> (%d,%d)\n",i,po[po[i].fa].len+1,po[i].len);
}
build_tree(root,1,n); getsi(1);
//qury(root,1,n,1); printf("cknow %lld\n",ansv[1]);
for (int i=1;i<=n;i++) sum[i]=sum[i-1]+v[i];
sum[0]=v[0];
//for (int i=1;i<=tot;i++)
//printf("kkkqqq (%d,%d) 1=%lld 2=%lld 3=%lld 4=%lld\n",po[po[i].fa].len+1,po[i].len,ma[i],cima[i],mi[i],cimi[i]);
for (int i=0;i<n;i++)
{
if (i) qury(root,1,n,i);
else ansv[i]=max(vvv[n]*vvv[n-1],vvv[1]*vvv[2]);
//if (i==1) printf("kqkq %lld\n",ansv[i]);
if (!sum[i]) ansv[i]=0;
printf("%lld %lld\n",sum[i],ansv[i]);
}
}
}SAM;
int main()
{
scanf("%d%s",&n,s+1); SAM.init();
for (int i=1;i<=n;i++) scanf("%lld",&vvv[i]);
for (int i=1;i<=n;i++) SAM.insert(s[n-i+1]-'a',vvv[n-i+1]);
sort(vvv+1,vvv+n+1);
SAM.solve();
return 0;
}
来源:CSDN
作者:syh0313
链接:https://blog.csdn.net/syh0313/article/details/103487644