[HAOI2011]Problem c

[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();
}

posted @ 2019-04-28 22:28  a1b3c7d9  阅读(109)  评论(0编辑  收藏  举报