zoukankan      html  css  js  c++  java
  • [BZOJ2392][HAOI2011]Problem c

    Description

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

    Input

    第一行一个整数T,表示数据组数

    对于每组数据,第一行有三个整数,分别表示n、m、M

    若m不为0,则接下来一行有m对整数,p1、q1,p2、q2 ,…, pm、qm,其中第i对整数pi、qi表示第pi个人的编号必须为qi

    Output

    对于每组数据输出一行,若是有解则输出YES,后跟一个整数表示方案数mod M,注意,YES和数之间只有一个空格,否则输出NO

    Sample Input

    2

    4 3 10

    1 2 2 1 3 1

    10 3 8882

    7 9 2 9 5 10


    Sample Output



    YES 4

    NO



    HINT



    100%的数据满足:1≤T≤10,1≤n≤300,0≤m≤n,2≤M≤109,1≤pi、qi≤n   且保证pi互不相同。


    还是考虑关于一个排列的的计数问题有两种基本的思路,一是考虑一个元素放在哪一个位置,二是考虑一个位置可以放哪些元素。

    对于这道题用第二种思路比较好解决。

    我们发现,问题有解的必要条件是在第$i$个位置,可以在$i$及之前放置的人的个数大于等于$i$。

    所以我们维护前缀和$sum[i]$表示编号确定在$i$之前的人的个数,特别的如果一个人没有确定的编号,那么它的最小编号就是0,所以$sum[0]=n-m$。

    设$cnt[i]$为编号必须是i的人的个数。

    然后设$f[i][j]$表示前$i$个位置,已经安排完了$j$个人的方案数,显然$j>i$。

    那么我们转移就是枚举$i$这个位置放$k$个人然后$f[i][j] = f[i][j]+f[i-1][j-k] imes C(sum[i]-(j-k)-cnt[i], k-cnt[i])$。

    意思就是刨去必须在这个位置上选的数。

     


    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    #define reg register
    inline int read() {
        int res = 0;char ch=getchar();bool fu=0;
        while(!isdigit(ch))fu|=(ch=='-'),ch=getchar();
        while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48), ch=getchar();
        return fu?-res:res;
    }
    #define ll long long
    int n, m, mod;
    ll C[310][310];
    ll f[301][301], cnt[310], sum[310];
    bool ok;
    
    int main()
    {
        int T = read();
        while(T--)
        {
            n = read(), m = read(), mod = read();
            memset(f, 0, sizeof f), memset(C, 0, sizeof C);
            memset(cnt, 0, sizeof cnt), memset(sum, 0, sizeof sum);
            ok = 1; 
            C[0][0] = 1;
            for (reg int i = 1 ; i <= n ; i ++) 
            {
                C[i][0] = 1;
                for (reg int j = 1 ; j <= i ; j ++) C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
            }
            for (reg int i = 1 ; i <= m ; i ++) read(), cnt[read()]++;
            sum[0] = n - m;
            for (reg int i = 1 ; i <= n ; i ++) sum[i] = sum[i - 1] + cnt[i];
            for (reg int i = 1 ; i <= n ; i ++) if (sum[i] < i) {ok = 0;break;}
            if (!ok) {puts("NO");continue;}
            f[0][0] = 1;
            for (reg int i = 1 ; i <= n ; i ++)
                for (reg int j = i ; j <= sum[i] ; j ++)
                    for (reg int k = cnt[i] ; j - k >= i - 1 ; k ++)
                        f[i][j] = (f[i][j] + f[i - 1][j - k] * C[sum[i] - (j - k) - cnt[i]][k - cnt[i]]) % mod;
            printf("YES %lld
    ", f[n][n]);
        }
        return 0;
    }
  • 相关阅读:
    年轻人的第一个 Spring Boot 应用,太爽了!
    面试问我 Java 逃逸分析,瞬间被秒杀了。。
    Spring Boot 配置文件 bootstrap vs application 到底有什么区别?
    坑爹的 Java 可变参数,把我整得够惨。。
    6月来了,Java还是第一!
    Eclipse 最常用的 10 组快捷键,个个牛逼!
    Spring Cloud Eureka 自我保护机制实战分析
    今天是 Java 诞生日,Java 24 岁了!
    厉害了,Dubbo 正式毕业!
    Spring Boot 2.1.5 正式发布,1.5.x 即将结束使命!
  • 原文地址:https://www.cnblogs.com/BriMon/p/9878374.html
Copyright © 2011-2022 走看看