[HAOI2011]Problem c

本文深入解析了HAOI2011竞赛中Problemc题目,探讨了如何在已知部分人员座位编号的情况下,计算剩余人员合法座位安排的方案数量。通过动态规划算法,计算在特定约束条件下的排列组合数,最终输出结果除以给定模数后的余数。

2302: [HAOI2011]Problem c

Description

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


首先对于每对\(,p_i, q_i\)显然\(p_i\)是没有用的

\(f[i][j]\) 表示前 \(i-n\) 个里安排了\(j\)个的个数

\(v[i]\)表示编号被钦定\(i\)的小朋友的个数,\(d[i]=\sum_1^iv[i]\)

\[\forall j\in [v[i],n-i+1]\ |\ f[i][j]=\sum_{k=0}^{j-v[i]} f[i+1][k]*C_{n-k-d[i]}^{j-k-v[i]}\]


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define LL long long 
using namespace std;

int g,i,m,n,j,k,a[100001],t,f[401][401],M,c[401][401],b,v[401],d[401];

int main()
{
    scanf("%d",&t);
    
    for(;t;t--)
    {
        scanf("%d%d%d",&n,&m,&M);
        memset(v,0,sizeof(v));
        memset(f,0,sizeof(f));
        memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++)   
        {
            c[i][i]=c[i][0]=1;
            for(int j=1;j<i;j++) c[i][j]=((LL)c[i-1][j]+c[i-1][j-1])%M;
        }
        for(int i=1;i<=m;i++) scanf("%d%d",&k,&a[i]);
        sort(a+1,a+1+m); j=1; k=0; b=0;
        for(int i=1;i<=n;i++) while(a[j]==i) v[i]++, j++; 
        for(int i=n;i>=1;i--) 
        {
            k+=v[i];
            if(k>n-i+1) {printf("NO\n"); b=1; break;}
        }
        if(b) continue;     
        for(int i=1;i<=n;i++) d[i]=d[i-1]+v[i];
        g=n-k; f[n+1][0]=1;
        for(i=n;i>=1;i--)
            for(j=v[i];j<=n-i+1;j++)
                for(k=0;k<=j-v[i];k++) f[i][j]=(f[i][j]+(LL)f[i+1][k]*c[n-k-d[i]][j-k-v[i]]%M)%M;
        
        printf("YES %d\n",f[1][n]);
    }
}

转载于:https://www.cnblogs.com/ZUTTER/p/10197112.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值