[HAOI2011]Problem c
给编号1~n的人安排座位,给它们各自选择1~n的座位号,可以重复,按照人的编号先后按其座位号入座,如果该个座位被占据了,就移向下下个座位,如果下下个也被占据了,就再向下下下个移动,以此类推,如果一直到第n个座位也被占据了,该种座位号选择方案就不合理。有T组询问,给出m个人,告诉你它们已经固定选择了座位号,询问合理座位号派给方案mod M。
100%的数据满足:1≤T≤10,1≤n≤300,0≤m≤n,2≤M≤10^9。
解
最暴力的方法为枚举排列,判断是否合法,注意到判断是否合法的方式很复杂,为一动态的过程,所以寻求状态量,发现不合法的状态即存在大于座位号i的人数选择超过n-i+1个,于是考虑以此为递推方程,自然设\(f[i][j]\)表示编号1~j的人选择大于等于座位号i方案数,再考虑到有人选择固定了,设\(s[i]\)表示已经固定中的人,选择座位号大于等于i的人数。
转移自然与座位号i+1有关,需要枚举座位号i选了几个人k,而j个人的标号可以自由交换,乘上\(C_j^k\),于是有
\[f[i][j]=\sum_{k=0}^{^j}f[i+1][j-k]C_{n-(j-k)}^{k}(j+s[i]\leq n-i+1)
\]
边界\(f[n+1][0]=1\)
答案\(f[0][n-m]\)
注意s[i]不满足条件应该提前break。
参考代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#define il inline
#define ri register
#define ll long long
using namespace std;
int s[305],dp[305][305],
c[305][305];
il void read(int&);
int main(){
int lsy,n,m,yyb;
int i,j,k;read(lsy);
while(lsy--){
read(n),read(m),read(yyb),dp[n+1][0]=1;
for(i=0;i<=n;++i){
c[i][0]=1;
for(j=1;j<=i;++j)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%yyb;
}
for(i=1;i<=m;++i)read(j),read(k),++s[k];
for(i=n;i;--i){s[i]+=s[i+1];if(s[i]>n-i+1){puts("NO");goto clear;}}
for(i=n;i;--i)
for(j=n-i+1-s[i];j>=0;--j)
for(k=0;k<=j;++k)
(dp[i][j]+=(ll)dp[i+1][j-k]*c[n-j+k][k]%yyb)%=yyb;
printf("YES %d\n",dp[1][n-m]);
clear:memset(s,0,sizeof(s)),memset(dp,0,sizeof(dp));
}
return 0;
}
il void read(int&x){
x&=0;ri char c;while(c=getchar(),c<'0'||c>'9');
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}