2019.01.28【NOIP提高组】模拟B组 JZOJ 4227 B

版权声明:虽然本蒟蒻很菜,但各位dalao转载请注明出处谢谢。 https://blog.csdn.net/xuxiayang/article/details/86683079

D e s c r i p t i o n Description

给定后缀数组 s a sa ,再给定第 i i 个位置选择 j j 这个字母的价值 w [ i ] [ j ] w[i][j] ,求满足该后缀数组的字符串的最大价值和

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


S o l u t i o n Solution

先利用 s a sa r a n k rank (不了解后缀数组的自行百度)
f [ i ] [ j ] f[i][j] 表示已经确定了 i i 个字母,第 i i 个位置选 j j 的最大价值和,设 k ( k < j ) k(k<j) 表示上一次选择的字母,因为此时的 k < j k<j ,所以是一定可以转移的,得到方程:

f [ i ] [ j ] = m a x { f [ i 1 ] [ k ] } + w [ s a [ i ] ] [ j ] f[i][j]=max\{f[i-1][k]\}+w[sa[i]][j]

但是,还有 k = j k=j 的情况,这种情况需要我们比较它们的字典序,但如果暴力比较时间复杂度是 O ( n ) O(n) 的,其实,我们可以利用 r a n k rank 数组 O ( 1 ) O(1) 判断是否合法即可。


C o d e Code

#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);
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/86683079