bzoj 4606 DNA Posted on 2019-06-04 | Views: $dp$ 计数. 求第 $R$ 大,可以想到把某一类的方案数全部算出来,用 $R$ 去减,就和用平衡树求第 $k$ 大,用 $k$ 减 $siz$ 的操作类似. 题面都明示了? 设 $f(i,j,k)$ 表示第 $i$ 个字符填 $j$ ,至少需要分成 $k$ 个不下降段的方案数.倒着 $dp$ 即可. 最后就从前往后匹配,一边匹配一边减就好了. 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778#include<bits/stdc++.h>using namespace std;typedef long long ll;inline ll read(){ ll out=0,fh=1; char jp=getchar(); while ((jp>'9'||jp<'0')&&jp!='-') jp=getchar(); if (jp=='-') fh=-1,jp=getchar(); while (jp>='0'&&jp<='9') out=out*10+jp-'0',jp=getchar(); return out*fh;}const int MAXN=5e4+10;int n,m,a[MAXN],id[400];char s[MAXN],invid[5]={'A','C','G','T'};ll f[MAXN][5][11],R;int main(){ for(int i=0;i<4;++i) id[invid[i]]=i; id['N']=4; n=read(),m=read(); R=read(); scanf("%s",s+1); for(int i=1;i<=n;++i) a[i]=id[s[i]]; if(a[n]==4) for(int i=0;i<4;++i) f[n][i][1]=1; else f[n][a[n]][1]=1; for(int i=n-1;i>=1;--i) { if(a[i]==4) { for(int j=0;j<4;++j) for(int k=1;k<=m;++k) for(int l=0;l<4;++l) f[i][j][k]+=f[i+1][l][k-(j>l)]; } else { for(int k=1;k<=m;++k) for(int l=0;l<4;++l) f[i][a[i]][k]+=f[i+1][l][k-(a[i]>l)]; } } for(int i=1;i<=n;++i) for(int j=0;j<4;++j) for(int k=1;k<=m;++k) f[i][j][k]+=f[i][j][k-1]; a[0]=0; for(int i=1;i<=n;++i) { if(a[i]==4) { for(int j=0;j<4;++j) { ll tmp=(j<a[i-1])?f[i][j][m-1]:f[i][j][m]; if(R>tmp) R-=tmp; else { a[i]=j; break; } } } if(a[i]<a[i-1]) --m; printf("%c",invid[a[i]]); } puts(""); return 0;}