[HAOI2011] Problem c
Description
有\(n\)个人\(n\)个座位,需要给每个人确定一个\(1-n\)的编号,编号可以相同。
接着从第一个人开始依次入座,每个人会尝试坐到\(a_i\),如果\(a_i\)被占据了,就尝试\(a_{i+1},a_{i+2}\dots a_n\)。如果尝试到第\(n\)个还不行,这个安排方案就不合法。同时有\(m\)个人的编号已经确定了,只能安排剩下的人的编号。求合法的安排方案。
Solution
首先考虑什么是不合法的方案。
设\(sum[i]\)表示最多可以让多少人编号\(\leq i\)
如果\(sum[i]<i\)那该方案就不合法
正确性挺显然的,就是入座的时候只可能编号小的人坐到编号大的座位上,不能从大到小坐。如果\(sum[i]<i\),那就是再怎么坐前\(i\)个也坐不满,肯定不合法。
然后考虑DP解决这个问题。考虑每个位置可以放哪些元素。
沿用刚才的状态设计思路,设\(f[i][j]\)表示有\(j\)个人编号\(\leq i\)的方案数,那么\(i\leq j\leq sum[i]\)。
枚举编号恰好为\(i\)的有\(k\)个,那么\(cnt[i]\leq k\leq j-(i-1)\),其中\(cnt[i]\)表示钦定编号为\(i\)的个数,\(j-(i-1)\)是至少要给前\(i-1\)个位置留\(i-1\)个人来填满。
那转移就是\(f[i][j]+=f[i-1][j-k]\times C(sum[i]-cnt[i]-j+k,k-cnt[i])\),\(sum[i]-cnt[i]-j+k\)的意义是,给第\(i\)个最多留出来\(sum[i]-cnt[i]\)个空位,再减去前\(i-1\)个用过的\(j-k\)个就是这么多了。
Code
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<cctype>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using std::min;
using std::max;
using std::swap;
using std::vector;
const int N=305;
typedef double db;
typedef long long ll;
#define pb(A) push_back(A)
#define pii std::pair<int,int>
#define all(A) A.begin(),A.end()
#define mp(A,B) std::make_pair(A,B)
int sum[N],c[N][N];
int f[N][N],ZYZ,cnt[N];
int getint(){
int X=0,w=0;char ch=0;
while(!isdigit(ch))w|=ch=='-',ch=getchar();
while( isdigit(ch))X=X*10+ch-48,ch=getchar();
if(w) return -X;return X;
}
signed main(){
int T=getint();
while(T--){
memset(f,0,sizeof f);
memset(c,0,sizeof c);
memset(cnt,0,sizeof cnt);
memset(sum,0,sizeof sum);
int n=getint(),m=getint();ZYZ=getint();
for(int i=1;i<=m;i++)
getint(),cnt[getint()]++;
sum[0]=n-m;int flag=0;
for(int i=1;i<=n;i++) {
sum[i]=sum[i-1]+cnt[i];
if(sum[i]<i) {
flag=1;
printf("NO\n");
break;
}
} if(flag) continue;
c[0][0]=1;
for(int i=1;i<=n;i++){
c[i][0]=1;
for(int j=1;j<=n;j++)
c[i][j]=(c[i-1][j-1]+c[i-1][j])%ZYZ;
}
f[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=i;j<=sum[i];j++){
for(int k=cnt[i];k<=j-i+1;k++)
f[i][j]=(1ll*f[i][j]+1ll*f[i-1][j-k]*c[sum[i]-cnt[i]-j+k][k-cnt[i]]%ZYZ)%ZYZ;
}
} printf("YES %d\n",f[n][n]);
} return 0;
}
当你走进这欢乐场