题目链接:https://www.luogu.org/problem/CF1183H 题意:给你一个长为n(100)的字符串,你需要找出k(1e12)个它本质不同的子序列,且没找出一个子序列的花费为n-len(子序列),求最小花费 分析
题目链接:https://www.luogu.org/problem/CF1183H
题意:给你一个长为n(100)的字符串,你需要找出k(1e12)个它本质不同的子序列,且没找出一个子序列的花费为n-len(子序列),求最小花费
分析:很明显想让花费最小,就从长度最大的子序列开始找,就转化成找每个长度的本质不同的子序列有多少个
我们用dp[i][j]表示从第i个字符开始,长度为j的子序列个数有多少个。
直接转移的话会出现重复,比如baa,你就会找出两个ba
所以对于每一个i,我们用一个vis数组标记,只转移相同字符第一个出现的那一个
为了保证序列是一个个加的,我们dp的第一维是长度Len
之后贪心寻找答案即可
注意:因为空串也是一个子序列,初始化的时候i=0的也要考虑,并且之后每个非空序列其实都相当于包含了一个空串,循环dp求解的时候也从i=0开始,最后寻找答案的时候就直接从dp[0][len]到dp[0][1]了
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int mod=1e9+7; const int maxn=2e5+7; const int N=1e4; char s[110]; ll dp[210][210]; int vis[100]; int main(){ int n;ll k;cin>>n>>k; k--;//第一个选的全串没有花费 scanf("%s",s+1); for(int i=0;i<=n;i++)dp[i][1]=1; for(int len=2;len<=n;len++){ for(int i=0;i<=n;i++){ memset(vis,0,sizeof(vis)); for(int k=i+1;k<=n;k++){ if(!vis[s[k]-‘a‘]){ vis[s[k]-‘a‘]=1; dp[i][len]+=dp[k][len-1]; } } } } ll ans=0; //cout<<dp[0][4]<<" 233\n"; for(int len=n;len>0&&k!=0;len--){ if(dp[0][len]<=k){ k-=dp[0][len]; ans+=1ll*(n+1-len)*dp[0][len]; } else{ ans+=1ll*(n+1-len)*k; k=0; } } if(k>0)printf("-1\n"); else printf("%I64d\n",ans); return 0; }