[HAOI2011] Problem C

(n) 个人安排座位,每个人手上有一个编号 (a_i)。从第一个人开始依次进场,每个人进场后,会从 (a_i) 往后找到第一个空着的座位就做,如果没找到则这个方案不合法。现在有 (m) 个人的座位已经确定(不同人的编号可以相同),求有多少种合法的安排方案。 (Tleq 10, n leq 300)

Solution

考虑怎样的情况的是无解的,设 (s_i) 表示编号既定的 (m) 人中编号 (geq i) 的人数,对于 (s_i>n-i+1) 显然是无解的

类似地,设 (f[i][j]) 为普通人中,编号 (geq i) 的人中确定了 (j) 个人的编号的方案数,那么考虑在确定了 (j-k) 人编号的情况下,选择 (k) 人编号为 (i) 来进行转移

[f[i][j]=sum_{k=0}^j f[i+1][j-k]cdot C_j^k, 0leq jleq n-s_i-i+1 ]

答案就是 (f[1][n-m])

注意无解一定要先判掉,不能等最后利用答案判。

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 505;

int T,n,m,mod,p[N],q[N],f[N][N],c[N][N],s[N];

signed main() {
    ios::sync_with_stdio(false);
    cin>>T;
    while(T--) {
        memset(s,0,sizeof s);
        memset(f,0,sizeof f);
        memset(c,0,sizeof c);
        cin>>n>>m>>mod;
        c[0][0]=1;
        for(int i=1;i<=n;i++) {
            c[i][0]=1;
            for(int j=1;j<=i;j++) {
                c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
            }
        }
        for(int i=1;i<=m;i++) {
            cin>>p[i]>>q[i];
            s[q[i]]++;
        }
        for(int i=n;i>=0;--i) s[i]+=s[i+1];
        int flag=1;
        for(int i=1;i<=n;i++) if(s[i]>n-i+1) flag=0;
        if(flag==0) {
            cout<<"NO"<<endl;
            continue;
        }
        f[n+1][0]=1;
        for(int i=n;i>=1;--i) {
            for(int j=0;j<=n-s[i]-i+1;j++) {
                for(int k=0;k<=j;k++) {
                    (f[i][j]+=f[i+1][j-k]*c[j][k])%=mod;
                }
            }
        }
        cout<<"YES "<<f[1][n-m]<<endl;
    }
}

原文地址:https://www.cnblogs.com/mollnn/p/12392042.html