Bzoj2302--Haoi2011Problem c

可以注意到题目中所说合法序列的充要条件是对于所有i,<=i的数至少出现过i次

那么我们可以设dp[i][j]代表小于i的数已经出现了j次

那么转移方程就很显然了

dp[i][j]=segma(dp[i-1][k]*C[j-k-ap[i]][n-m-(k-tl[i-1])])  (k+ap[i]<=j)

ap[i]为确定为i的个数,tl[i]为确定小于i的个数

代码:

#include<bits/stdc++.h>
#define LL long long
#define INF 1000000000
using namespace std;
inline double _fabs(double a) {return a>0?a:-a;}

inline int read() {
    int ret=0,f=1;char c=getchar();
    while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
    while(c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
    return ret*f;
}
inline int gcd(int a,int b) {
    int t;
    while(b) {t=a;a=b;b=t%b;}
    return a;
}

#define fix 1000
#define MAXN 305
#define MAXM 10005

int n,m,ap[MAXN],tl[MAXN],T,M;LL dp[MAXN][MAXN];

void init() {
    memset(dp,0,sizeof(dp));
    memset(ap,0,sizeof(ap));
}

int C[MAXN][MAXN];
void pre() {
    for(int i=0;i<MAXN;i++) C[0][i]=C[i][0]=1;
    for(int i=1;i<MAXN;i++) for(int j=1;j<MAXN;j++) 
        C[i][j]=(C[i-1][j]+C[i][j-1])%M;
}

bool check() {
    int now=0;
    for(int i=n;i>0;i--) {
        now+=ap[i];
        if(now>n-i+1) return 1;
    }
    return 0;
}

int main() {
    T=read();
    while(T--) {
        init();
        n=read();m=read();M=read();
        for(int i=1;i<=m;i++) read(),ap[read()]++;
        if(check()) {puts("NO");continue;}
        m=n-m;pre();dp[0][0]=1;
        for(int i=1;i<=n;i++) tl[i]=tl[i-1]+ap[i];
        for(int i=1;i<=n;i++) 
            for(int j=i;j<=n;j++) 
                for(int k=tl[i];k<=j;k++){
                    if(j<tl[i]) break;
                    dp[i][j]+=dp[i-1][k-ap[i]]*C[j-k][m-j+tl[i]]%M;
                    dp[i][j]%=M;
                }
        printf("YES %d\n",dp[n][n]);
    }
    return 0;
}

代码不知道哪里写丑了还是什么,慢了一个档。。。

posted @ 2016-10-24 16:12  ihopenot  阅读(178)  评论(0编辑  收藏  举报