https://www.codechef.com/FEB14/problems/LEMOVIE
题意:
对于一个序列,定义其“激动值”为序列中严格大于前面所有数的元素的个数。
给定n个数p1;,p2... pn,求这n个数的所有排列中,激动值不超过k的个数。$1 k le n le 200,1 le pi le 200$
这种题有一个很神的想法:
把排列按某种顺序往里插入,使得后不会影响前
对于本题,先离散化去重后,从大到小插入,后插入的元素不会影响已经插入的元素严格大于前面所有数
$f[i][j]$表示插入了前$i$大的数,激动值为$j$的方案数
激动值改变只有可能是当前要插入的数中有一个放在了最前面
转移时分类讨论有没有数插在最前面,需要用到隔板法,
设已经插入$x$个数,第$i$大的有$y$个数
$f(i,j)=f(i-1,j)*inom{x+y-1}{x-1}*{y!} + f(i-1,j-1)*inom{x+y-1}{x}*{y!}$
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int N=205,INF=1e9+5,P=1e9+7; inline int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } int n,k,a[N]; int m,d[N],s[N]; inline bool cmp(int a,int b){return a>b;} ll c[N][N],fac[N]; inline void mod(ll &x){if(x>=P) x-=P;} void ini(){ c[0][0]=1;fac[0]=1; for(int i=1;i<=200;i++){ c[i][0]=1; for(int j=1;j<=i;j++) mod(c[i][j]+=c[i-1][j]+c[i-1][j-1]); fac[i]=fac[i-1]*i%P; } } ll f[N][N]; void dp(){ f[0][0]=1; for(int i=1;i<=m;i++) for(int j=1;j<=k;j++){ int x=s[i-1],y=d[i]; f[i][j]=f[i-1][j]*c[x+y-1][x-1]%P*fac[y]%P; mod(f[i][j]+=f[i-1][j-1]*c[x+y-1][x]%P*fac[y]%P); //printf("f %d %d %lld ",i,j,f[i][j]); } ll ans=0; for(int i=1;i<=k;i++) mod(ans+=f[m][i]); printf("%lld ",ans); } int main(){ freopen("in","r",stdin); ini(); int T=read(); while(T--){ memset(d,0,sizeof(d)); n=read();k=read(); for(int i=1;i<=n;i++) a[i]=read(); sort(a+1,a+1+n,cmp); m=0; d[++m]=1; for(int i=2;i<=n;i++){ if(a[i]==a[i-1]) d[m]++; else d[++m]=1; } for(int i=1;i<=m;i++) s[i]=s[i-1]+d[i]; //for(int i=1;i<=m;i++) printf("%d ",d[i]);puts(""); //for(int i=1;i<=m;i++) printf("%d ",s[i]);puts(""); dp(); } }