首页 > 代码库 > 51nod 1231 记分牌

51nod 1231 记分牌

链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1231

一个得分合法等价于前k小的得分之和大于等于$\frac{k*(k-1)}{2}$

听说是竞赛图的经典结论,可是我不会证。

技术分享
#include<cstdio>
#include<cstring>
#include<algorithm>
#define w(x) (x)*(x-1)/2
using namespace std;

int read_p,read_ca,read_f;
inline int read(){
    read_p=0;read_ca=getchar();read_f=1;
    while(read_ca<0||read_ca>9) {if (read_ca==-) read_f=-1;read_ca=getchar();}
    while(read_ca>=0&&read_ca<=9) read_p=read_p*10+read_ca-48,read_ca=getchar();
    return read_p*read_f;
}
const int MOD=1e9+7;
int T,n,m,mmh[42][42][1611],a,t[42],q[42],C[42][42];
inline void M(int &x){while(x>=MOD)x-=MOD;}
int main(){
    register int i,j,k,l;
    T=read();
    C[0][0]=1;
    for (i=1;i<=40;i++)
    for (C[i][0]=1,j=1;j<=i;j++) M(C[i][j]=C[i-1][j]+C[i-1][j-1]);
    while (T--){
        n=read();
        memset(mmh,0,sizeof(mmh));memset(t,0,sizeof(t));
        for (i=0;i<n;i++){
            a=read();
            if (a!=-1) t[a]++;else t[n]++;
        }
        if (t[0]>1) {puts("0");continue;}
        if (t[0]==0) if (mmh[0][0][0]=1,t[n]) mmh[1][0][0]=C[t[n]][1];
        if (t[0]==1) mmh[1][0][0]=1;q[0]=t[0];
        for (i=1;i<n;i++) q[i]=q[i-1]+t[i];
        for (i=0;i<=n;i++)
        for (j=1;j<n;j++)
        for (k=0;k<=n*n;k++)
        if (mmh[i][j-1][k]){
            for (l=0;l<=t[j];l++) if (k+j*l<w(i+l)) break;
            if (l<=t[j]) continue;
            for (l=t[j];l<=q[j]+t[n]-i;l++)
            if (k+j*l>=w(i+l)) M(mmh[i+l][j][k+j*l]+=1LL*mmh[i][j-1][k]*C[t[n]-(i-q[j-1])][l-t[j]]%MOD);
        }
        printf("%d\n",mmh[n][n-1][w(n)]);
    }
}
View Code

 

51nod 1231 记分牌