JZOJ4227【五校联考3day2】B

博客探讨了后缀数组的概念,以及如何根据后缀数组和一个价值矩阵构造出一个字符串,使每个位置的价值和最大化。文章给出了输入输出格式、样例及解题思路,并涉及动态规划的解决方法。
摘要由CSDN通过智能技术生成

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&output

为了减少输入量,部分数据将在程序内生成。
有一个随机数产生器,有个内部变量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

输入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

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

hint提示

对于前20%的数据,n ≤ 5。
对于前40%的数据,n ≤ 15。
对于前60%的数据,n ≤ 1000。
对于前100%的数据,n ≤ 100000,0 ≤ x_0 ≤ 998244353。保证存在一个仅含小写英文字母的字符串,使得它的后缀数组为A。

solution

字符串S
后缀数组a53142
后缀数组的数值在数组中的位置rank35241

#后缀数组

把一个字符串的所有非空后缀按字典序从小到大排序,然后按顺序排列出后缀的第一个字符在原串中的位置所形成的数组,就是后缀数组

子串有(某位是下标)
a–5
ba–4
aba–3
baba–2
ababa–1
排序后,下标{5,3,1,4,2}就是后缀数组
#价值

以及一个n × 26 的矩阵w,所有下标都从1开始,其中w_{i, j}表示第i个位置填第j个小写字母的价值,现在你需要给出一个长度为n的字符串,使得它的后缀数组是A,而且它每个位置的价值和最大。
emmmm
接着你将按照先i递增,再j递增的顺序生成w_{i, j}。每次生成一个随机数r,则w_{i, j} = r mod {10^4}。

消化一下,像这样

void random(ll &x){x=(100000005*x+1532777326)%998244353;return;}

然后预处理 W i , j W{i,j} Wi,j

for(int i=1;i<=n;++i)
		for(int j=1;j<=26;++j)
			random(x),w[i][j]=x/100%10000;

好了
然后开始主要部分
100%
dp
F i , j F{i,j} Fi,j 表示到了后缀数组第i位,填到了第j个字母的最大价值
易得转移方程
f[i][j]=max{f[i][j],f[i-1][k]}(1<=k<j)
如果rank[a[i]+1]>rank[a[i-1]+1]那么证明
f[i-1][j]转移到f[i][j]也是可行的
那么有代码段

for(int k=1;k<j;k++)f[i][j]=max(f[i][j],f[(i-1)][k]);
			if(rank[a[i]+1]>rank[a[i-1]+1])f[i][j]=max(f[i][j],f[(i-1)][j]);
			f[i][j]+=w[a[i]][j];

最后答案就是max{f[n][i]}(1<=i<=26)
参考代码
tips:!!!玄学!滚动数组会错的!!!

#include <cstdio>
#define ll long long
#define MAXN 100010

using namespace std;

ll n,a[MAXN],rank[MAXN],f[MAXN][30],w[MAXN][30],ans=-1<<30,x;

void random(ll &x){x=(100000005*x+1532777326)%998244353;return;}

ll max(ll a,ll b){return a>b?a:b;}

int main()
{
	freopen("data3.in","r",stdin);
	scanf("%lld%lld",&n,&x);
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]),rank[a[i]]=i;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=26;++j)
			random(x),w[i][j]=x/100%10000;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=26;j++)
		{
			for(int k=1;k<j;k++)f[i][j]=max(f[i][j],f[(i-1)][k]);
			if(rank[a[i]+1]>rank[a[i-1]+1])f[i][j]=max(f[i][j],f[(i-1)][j]);
			f[i][j]+=w[a[i]][j];
		}
	}
	for(int i=1;i<=26;i++)
		ans=max(f[n][i],ans);
	printf("%lld",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值