zoukankan      html  css  js  c++  java
  • bzoj 2302: [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互不相同。

    题解:

      这个题目,当然是一个十分难想的DP。

      我们如果不对判断条件进行转化而直接记下所以空的位置进行转移,就只能写状压了。当然没有什么分。

      所以我们考虑对于一个位置i,如果有超过i个人的选择的位置<=i那么i这个位置才有可能合法(想想就明白了),如果能保证每个位置都和法,那么这个方案才是合法的。

      那么也就是说题目转化成了,求一个序列,对于每个i位置,都有超过i个人选择i之前的位置或者i。那么就可以dp了。

      dp[i][j]表示i这个位置之前(包括i)有j个人选择的方案数,那么显然我们必须用合法的方案进行转移,所以j>=i,那么我们枚举i这一位有多少人选k,然后再枚举j,那么上一位就有j-k个人选,所以是从dp[i][j-k]转移过来,然后我们乘以一个组合数就可以了,有细节,想想就明白了。

    代码:

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <iostream>
    #define MAXN 1000
    #define ll long long
    #define RG register
    using namespace std;
    ll C[MAXN][MAXN],dp[MAXN][MAXN],num[MAXN],sum[MAXN];
    int n,m;ll mod;
    
    void work(){
        scanf("%d%d%lld",&n,&m,&mod);
        memset(dp,0,sizeof(dp));memset(C,0,sizeof(C));memset(num,0,sizeof(num));memset(sum,0,sizeof(sum));
        C[0][0]=1;
        for(int i=1;i<=310;i++){C[i][0]=1;
            for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
        }
        for(int i=1;i<=m;i++){
            int x;scanf("%d%d",&x,&x);num[x]++;
        }
        sum[0]=n-m;
        for(int i=1;i<=n;i++) sum[i]=sum[i-1]+num[i];
        for(int i=1;i<=n;i++) if(sum[i]<i) {printf("NO
    ");return;}
        dp[0][0]=1;
        for(RG int i=1;i<=n;i++)
      for(RG int j=i;j<=sum[i];j++){
        for(RG int k=num[i];k<=j-i+1;k++)
         dp[i][j]+=dp[i-1][j-k]*C[sum[i]-num[i]-j+k][k-num[i]],dp[i][j]%=mod;
       }
        printf("YES %lld
    ",dp[n][n]);
    }
    
    int main()
    {
        int t;cin>>t;
        while(t--) work();
        return 0;
    }
  • 相关阅读:
    剑指offer——最小的K个数和数组中第K大的元素
    Leetcode刷题指南链接整理
    160. Intersection of Two Linked Lists
    100. Same Tree
    92. Reverse Linked List II
    94. Binary Tree Inorder Traversal
    79. Word Search
    78,90,Subsets,46,47,Permutations,39,40 DFS 大合集
    0x16 Tire之最大的异或对
    0x16 Tire
  • 原文地址:https://www.cnblogs.com/renjianshige/p/7636737.html
Copyright © 2011-2022 走看看