【Codeforces】Codeforces Round #570 (Div. 3) D. Messenger | 动态规划

111 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第25天,点击查看活动详情

【Codeforces】Codeforces Round #570 (Div. 3) D. Messenger | 动态规划

题目链接

Problem - H - Codeforces

题目

image.png

题目大意

对于给定的长度为 nn 的字符串 s1,s2,...,sns_1,s_2,...,s_n,定义其长度为 lenlen 的子序列(空串也是原字符串的一个子串)的权值为 nlenn-len。从原串中选择互不相同的 kk 个子序列,输出被选出来的子序列的权值和的最小值。

定义两个原串的子序列 a1,a2,...,alenaa_1,a_2,...,a_{lena}b1,b2,...,blenbb_1,b_2,...,b_{lenb} 相同,需要满足以下两个条件:

  1. lena=lenblena=lenb
  2. ai=bi(1ilena)a_i=b_i(1\le i\le lena)

思路

我们显然想要取尽可能长的子序列,于是先统计长度为 jj 的不互相同的子序列个数。这个东西可以用动态规划求解。

首先我们预处理出 pre[i][c]pre[i][c] 表示前 i1i-1 个元素中下标最大的字符 a+c(0c25)'a'+c(0\le c\le25) 所在的位置。可以简单地用如下方式计算:最初 pre[1][c]pre[1][c]1-1,然后对于从 11n1n-1 的每个 ii,都有:

  • 如果 a+csi'a'+c\neq s_ipre[i+1][c]=pre[i][c]pre[i+1][c]=pre[i][c]
  • 如果 a+c=si'a'+c=s_ipre[i+1][c]=ipre[i+1][c]=i

dp[i][j]dp[i][j] 表示以 ii 为右端点的长度为 jj 的不互相同的子序列个数,因为 pre[i][c]pre[i][c] 表示前 i1i-1 个元素中下标最大的字符 a+c'a'+c 所在的位置,所以 dp[pre[i][c]][j]dp[pre[i][c]][j] 表示前 i1i-1 个字符中以 a+c'a'+c 为结尾的长度为 jj 的子序列个数。

ii 为右端点的长度为 1 的子序列个数显然为 1,然后我们试图把 sis_i 放在每个长度为 j1j-1 的串的末尾。转移方程如下:

  • dp[i][j]=c=025(pre[i][c]!=1)dp[pre[i][c]][j1]dp[i][j]=\sum_{c=0}^{25} {(pre[i][c]!=-1)*dp[pre[i][c]][j-1]}

在统计完整个 dpdp 数组之后,让我们遍历从 nn11 的所有可能长度 jj 并计算长度为 jj 的子序列数 cntcnt。根据上述定义:

  • cnt=c=025(pre[n+1][c]!=1)dp[pre[n+1][c]][j]cnt=\sum_{c=0}^{25} {(pre[n+1][c]!=-1)*dp[pre[n+1][c]][j]}

则我们可以取走不超过 cntcnt 个长度为 jj 的串,假设我们取走了 xx 个,对答案的贡献就为 x×(nj)x\times (n-j),我们需要选择的更短的子序列的数量为 kxk-x

如果在所有迭代之后取了的子序列数量还不够,我们将空字符串添加到答案中,并将答案增加 nn。如果在此之后取了的子序列数量仍然不够,则答案为 -1,否则答案为计算的总和。

代码

#include <bits/stdc++.h>
using namespace std;
using LL=long long;
const int N=1001;
int n,m,tot;
LL k;
char ch[N];
int pre[N][26];
LL dp[N][N];
int main()
{
	scanf("%d%lld ",&n,&k);
	cin>>ch+1;
	for (int j=0;j<26;++j) 
		pre[1][j]=-1;
	for (int i=1;i<=n;++i)
	{
		for (int j=0;j<26;++j) pre[i+1][j]=pre[i][j];
		pre[i+1][ch[i]-'a']=i;
	}
	for (int i=1;i<=n;++i) dp[i][1]=1;
	for (int i=1;i<=n;++i)
	{
		for (int j=2;j<=i;++j)
		{
			for (int c=0;c<26;++c) dp[i][j]+=(pre[i][c]!=-1)*dp[pre[i][c]][j-1];
			dp[i][j]=min(dp[i][j],k);
		}
	}
	LL ans=0;
	for (int len=n;len>=1;--len)
	{
		LL cnt=0,x;
		for (int c=0;c<26;++c) cnt+=(pre[n+1][c]!=-1)*dp[pre[n+1][c]][len];
		x=min(cnt,k);
		ans+=x*(n-len);
		k-=x;
	}
	if (k==1) ans+=n,k=0;
	if (!k) printf("%lld",ans);
	else printf("-1");
	return 0;
}