51nod 1231 记分牌
链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1231
一个得分合法等价于前k小的得分之和大于等于$\frac{k*(k-1)}{2}$
听说是竞赛图的经典结论,可是我不会证。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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)]); } }