版权声明:虽然本蒟蒻很菜,但各位dalao转载请注明出处谢谢。 https://blog.csdn.net/xuxiayang/article/details/86683079
给定后缀数组 ,再给定第 个位置选择 这个字母的价值 ,求满足该后缀数组的字符串的最大价值和
对于前20%的数据,
。
对于前40%的数据,
。
对于前60%的数据,
。
对于前100%的数据,
。保证存在一个仅含小写英文字母的字符串,使得它的后缀数组为A。
先利用
求
(不了解后缀数组的自行百度)
设
表示已经确定了
个字母,第
个位置选
的最大价值和,设
表示上一次选择的字母,因为此时的
,所以是一定可以转移的,得到方程:
但是,还有 的情况,这种情况需要我们比较它们的字典序,但如果暴力比较时间复杂度是 的,其实,我们可以利用 数组 判断是否合法即可。
#pragma GCC optimize(2)
#include<algorithm>
#include<cstdio>
#include<cctype>
using namespace std;int sa[100011],rank[100011],n;
long long x,f[100011][26],ans,w[100011][26];
inline long long newi(){x=(100000005*x+1532777326)%998244353;return (int)x/100;}
inline char Getchar()
{
static char buf[10000000],*p1=buf+10000000,*pend=buf+10000000;
if(p1==pend)
{
p1=buf; pend=buf+fread(buf,1,10000000,stdin);
if (pend==p1) return -1;
}
return *p1++;
}
inline long long read()
{
char c;int d=1;long long f=0;
while(c=Getchar(),!isdigit(c))if(c==45)d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=Getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
inline bool check(int i){return rank[sa[i]+1]>rank[sa[i-1]+1];}
signed main()
{
scanf("%d%lld",&n,&x);
for(register int i=1;i<=n;i++) scanf("%d",sa+i),rank[sa[i]]=i;
for(register int i=1;i<=n;i++) for(register int j=0;j<26;j++) w[i][j]=newi()%10000;
for(register int i=0;i<26;i++) f[1][i]=w[sa[1]][i];
for(register int i=2;i<=n;i++)
for(register int j=0;j<26;j++)
{
for(register int k=0;k<j;k++) f[i][j]=max(f[i][j],f[i-1][k]+w[sa[i]][j]);
if(check(i)) f[i][j]=max(f[i][j],f[i-1][j]+w[sa[i]][j]);
}
for(register int i=0;i<26;i++) ans=max(ans,f[n][i]);
printf("%lld\n",ans);
}