Description
小D是雅礼高一著名的神犇,在NOI同步赛中获得了满分的优异成绩,而全国没有任何其他人获得如此的成绩。
某天晚上,高一内部在讨论一道题目,然而包括小D之内的各种神犇都毫无头绪,这时候,高二的人赢小T上来给高二进行了精彩的讲解。
小D被小T的神犇气场所折服,他知道小T之所以没有同步赛满分是不屑于,于是他决定拜小T为师。
一日小T正在给小D讲解后缀数组。
“把一个字符串的所有非空后缀按字典序从小到大排序,然后按顺序排列出后缀的第一个字符在原串中的位置所形成的数组,就是后缀数组。如“ababa”的后缀数组就是{5, 3, 1, 4, 2}。”
这里的位置从1开始编号,字符串仅包含小写英文字母。
接着小T给小D讲解了它的构造过程。
小D毕竟身为同步赛满分,水平还是不低,他立即举一反三:既然我们能给定一个字符串,给出他的后缀数组,那么给定后缀数组,能不能恢复字符串呢。
小T说:“这是不行的,这个问题我几年前研究过,譬如说,假设你后缀数组是{2, 1},那么原串既可以是“aa”,也可以是“ bb”。然而,我们的确可以提出一些有趣的问题,我记得我小学的时候,研究过一个问题,给定一个长度为n的数组A,以及一个n × 26 的矩阵w,所有下标都从1开始,其中w_{i, j}表示第i个位置填第j个小写字母的价值,现在你需要给出一个长度为n的字符串,使得它的后缀数组是A,而且它每个位置的价值和最大。}这个问题可不简单,我小学的时候研究了整整一节课。”
小D想了想,觉得自己大概就算在小学也只要一节课就想得出来。各位做题人你们会做吗?
Input
为了减少输入量,部分数据将在程序内生成。
有一个随机数产生器,有个内部变量x初始时为x_0,每次产生随机数时它会将x变为(100000005x + 1532777326) mod 998244353,然后返回x/100取下整。(a mod b 表示a除以 b的余数,该运算的优先级高于加减法。)
输入一行两个两个整数n, x_0。
首先输入一个1到n的排列,表示数组A,A的定义如题所述。
接着你将按照先i递增,再j递增的顺序生成w_{i, j}。每次生成一个随机数r,则w_{i, j} = r mod {10^4}。
Output
为了减少输出量,你只需要输出最大的价值和,不需要给出对应的字符串。
输出一行一个整数表示最大的价值和。
Sample Input
输入1: 1 493941464 1 输入2: 2 736594838 1 2 输入3: 5 910387714 1 2 3 4 5 输入4: 15 892431401 8 5 9 1 2 7 11 3 14 15 13 10 12 6 4
Sample Output
输出1: 9490 样例1解释: 答案即为权值最大的字母的权值,计算得f的权值为9490最大。前六个字母的权值依次为5602, 7113, 5633, 756, 8496, 9490。 输出2: 16658 样例2解释: 计算得“sw”的权值为16658最大,注意虽然“wf” 的权值是17957,但是它的后缀数组为{2, 1},不满足条件;“ww”的权值为16935,但由于“w” 的字典序小于“ww” 所以后缀数组也是{2, 1}。 输出3: 44455 样例3解释: 对应的字符串为“hoooq”。 输出4: 129724
Data Constraint
对于前20%的数据,n ≤ 5。
对于前40%的数据,n ≤ 15。
对于前60%的数据,n ≤ 1000。
对于前100%的数据,n ≤ 100000,0 ≤ x_0 ≤ 998244353。保证存在一个仅含小写英文字母的字符串,使得它的后缀数组为A。
Source / Author: 雅礼中学 毛啸 B
题解:
设f[i][j]为排名第i的后缀开头是j字母的最大权值。
f[i][j] 由f[i-1][k] 转移而来,转移有两种情况。
- 'a'<=k<j : f[i][j] = max(f[i-1][k] )+w[a[i]][j];
- k==j
重点是第二种情况怎么处理。
i-1的排名比i前,他们的开头又一样,因此i-1的长度比i短。
rank[i]表示第i个位置开始的后缀的排名,rank[A[i]]=i;
只需要判断rank[Ai + 1]是否大于 rank[A[i-1] +1]
具体意思就是
看Ai +1 这个位置的后缀排名,是否大于A[i-1] +1这个位置的后缀排名
若Ai+1这个位置的后缀排名大于A[i-1] +1这个位置的后缀排名,那么当k==j,两个字符串前面都加上同样的字符,字典序大小关系不变。
k==j :f[i][j] = max(f[i][j] , f[i-1][j] + w[a[i]][j])(rank[Ai + 1]> rank[A[i-1] +1])
时间复杂度:O(n)
注意i枚举的是A的下标
#include<bits/stdc++.h>
#define N 100010
#define inf 2147483647
#define rint register ll
#define point(a) multiset<a>::iterator
#define mod (ll)(1e9+7)
#define ll long long
#define mem(a,b) memset(a,b,sizeof (a))
#define open(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
using namespace std;
ll n,x,ans;
ll i,j,k;
ll f[N][30],w[N][30],rank[N],A[N];
void init()
{
for(rint i=1;i<=n;i++)rank[A[i]]=i;
return ;
}
void dp()
{
for(i=0;i<26;i++)f[1][i] = w[A[1]][i];
for(i=2;i<=n;i++)
{
for(j=0;j<26;j++)
{
for(k=0;k<j;k++) f[i][j] = max(f[i][j] , f[i-1][k] + w[A[i]][j]);
if(rank[A[i] + 1] > rank[A[i-1] +1])f[i][j] = max(f[i][j] , f[i-1][j] + w[A[i]][j]);
}
}
return ;
}
int main()
{
open("string");
scanf("%lld%lld",&n,&x);
for(i=1;i<=n;i++)scanf("%lld",&A[i]);
for(i=1;i<=n;i++)
for(j=0;j<26;j++) x=(x*100000005 + 1532777326) % 998244353,w[i][j]=x/100 % 10000;
ans=0;
init();
dp();
for(i=0;i<26;i++)ans=max(ans,f[n][i]);
printf("%lld\n",ans);
return 0;
}