[bzoj 2302--HAOI2011]Problem c

124 篇文章 2 订阅
26 篇文章 0 订阅

给n个人安排座位,先给每个人一个1~n的编号,设第i个人的编号为ai(不同人的编号可以相同),接着从第一个人开始,大家依次入座,第i个人来了以后尝试坐到ai,如果ai被占据了,就尝试ai+1,ai+1也被占据了的话就尝试ai+2,……,如果一直尝试到第n个都不行,该安排方案就不合法。然而有m个人的编号已经确定(他们或许贿赂了你的上司…),你只能安排剩下的人的编号,求有多少种合法的安排方案。由于答案可能很大,只需输出其除以M后的余数即可。

这道题很容易发现人的顺序是没有用的,只有编号出现的次数是有用的。只要满足sum[i]<=n-i+1和sum[1]=n(sum[i]表示编号>=i的个数)这两个条件就是合法的状态。
那就很容易发现这是dp,f[i][j]表示编号>=i的次数为j时的合法安排方案种数。dp方程再加一个组合数乱搞一下就可以了,那这道题就做完了。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline void write(int x)
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
inline void pr1(int x){write(x),putchar(' ');}
inline void pr2(int x){write(x),puts("");}
int cnt[310],sum[310],f[310][310],C[310][310];
int main()
{
    //freopen("2302.in","r",stdin);
    //freopen("2302.out","w",stdout);
    int T=read();
    while(T--)
    {
        memset(cnt,0,sizeof(cnt));memset(f,0,sizeof(f));
        int n=read(),m=read(),P=read();
        for(int i=1;i<=m;i++){int p=read(),q=read();cnt[q]++;}
        C[0][0]=1;
        for(int i=1;i<=300;i++)
        {
            C[i][0]=1;
            for(int j=1;j<=i;j++)C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
        }sum[n+1]=0;
        for(int i=n;i>=1;i--)sum[i]=sum[i+1]+cnt[i];
        bool bk=true;
        for(int i=1;i<=n;i++)if(i+sum[i]-1>n){bk=false;break;}
        if(bk==false){puts("NO");continue;}
        f[n+1][0]=1;
        for(int i=n;i>=1;i--)
        {
            for(int j=sum[i];i+j-1<=n;j++)
            {
                for(int k=sum[i]-cnt[i];k<=min(n-i,j-cnt[i]);k++)(f[i][j]+=1LL*f[i+1][k]*C[n-k-(sum[1]-sum[i+1])][j-k-cnt[i]]%P)%=P;
            }
        }
        printf("YES %d\n",f[1][n]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值