[LOJ 2133][UOJ 131][BZOJ 4199][NOI 2015]品酒大会
[LOJ 2133][UOJ 131][BZOJ 4199][NOI 2015]品酒大会
题意
给定一个长度为 \(n\) 的字符串 \(s\), 对于所有 \(r\in[1,n]\) 求出 \(s\) 的所有LCP不小于 \(r\) 的后缀对的个数以及这些后缀对所能组成的最大权值.
一个后缀对 \((a,b)\) 的权值是它们左端点的权值的积.
\(n\le 3\times 10^5\).
题解
很久以前写的SAM沙雕题
因为要求LCP所以我们把这个串reverse一下用SAM搞.
根据后缀自动机的性质, 某两个后缀的LCP就是它们在SAM上对应结点的LCA的 \(len\).
那么对于计数的部分, 我们显然只要对于每个点都算出有多少个后缀以它为LCA就可以了.
后面求最大权值的部分看上去好像只要记录一下子树中的最大值和次大值就可以了, 然而权值可能有负数于是还得记录最小值和次小值.
计算出每个 \(len\) 的贡献后取后缀和就可以出答案了.
参考代码
#include <bits/stdc++.h>
const int MAXN=6e5+10;
typedef long long int64;
struct Edge{
int from;
int to;
Edge* next;
};
Edge E[MAXN];
Edge* head[MAXN];
Edge* top=E;
int n;
int cnt=1;
int root=1;
int last=1;
int v[MAXN];
char s[MAXN];
int len[MAXN];
int prt[MAXN];
int val[MAXN];
int size[MAXN];
int64 ans[MAXN];
int64 sum[MAXN];
int maxv[MAXN][2];
int minv[MAXN][2];
std::map<char,int> chd[MAXN];
void DFS(int);
void Insert(int,int);
void Extend(char,int);
int main(){
memset(ans,0x80,sizeof(ans));
memset(maxv,0x80,sizeof(maxv));
memset(minv,0x7F,sizeof(minv));
scanf("%d",&n);
scanf("%s",s);
for(int i=0;i<n;i++)
scanf("%d",v+i);
for(int i=1;i<=n;i++)
Extend(s[n-i],v[n-i]);
for(int i=2;i<=cnt;i++)
Insert(prt[i],i);
DFS(root);
for(int i=n-1;i>=0;i--){
sum[i]+=sum[i+1];
ans[i]=std::max(ans[i],ans[i+1]);
}
for(int i=0;i<n;i++)
printf("%lld %lld\n",sum[i],sum[i]==0?0:ans[i]);
return 0;
}
void UpdateMax(int x,int v){
maxv[x][1]=std::max(maxv[x][1],v);;
if(maxv[x][0]<maxv[x][1])
std::swap(maxv[x][0],maxv[x][1]);
}
void UpdateMin(int x,int v){
minv[x][1]=std::min(minv[x][1],v);
if(minv[x][0]>minv[x][1])
std::swap(minv[x][0],minv[x][1]);
}
void DFS(int root){
for(Edge* i=head[root];i!=NULL;i=i->next){
DFS(i->to);
sum[len[root]]+=1ll*size[root]*size[i->to];
size[root]+=size[i->to];
UpdateMin(root,minv[i->to][0]);
UpdateMin(root,minv[i->to][1]);
UpdateMax(root,maxv[i->to][0]);
UpdateMax(root,maxv[i->to][1]);
}
if(size[root]>1)
ans[len[root]]=std::max(ans[len[root]],std::max(1ll*minv[root][0]*minv[root][1],1ll*maxv[root][0]*maxv[root][1]));
}
void Extend(char x,int v){
int p=last;
int np=++cnt;
size[last=np]=1;
len[np]=len[p]+1;
minv[np][0]=v;
maxv[np][0]=v;
while(p&&!chd[p].count(x))
chd[p][x]=np,p=prt[p];
if(p==0)
prt[np]=root;
else{
int q=chd[p][x];
if(len[q]==len[p]+1)
prt[np]=q;
else{
int nq=++cnt;
chd[nq]=chd[q];
prt[nq]=prt[q];
prt[q]=nq;
prt[np]=nq;
len[nq]=len[p]+1;
while(p&&chd[p][x]==q)
chd[p][x]=nq,p=prt[p];
}
}
}
void Insert(int from,int to){
top->from=from;
top->to=to;
top->next=head[from];
head[from]=top++;
}
本博客已弃用, 新个人主页: https://rvalue.moe, 新博客: https://blog.rvalue.moe