題目傳送門 - 51Nod1231題意
題解
顯然是一個競賽圖相關的題。
我們首先證明一個結論:
一個出度序列存在對應的 $n$ 個點的競賽圖的充分必要條件是:這個出度序列的所有元素之和為 $\cfrac{n(n-1)}{2}$ ,且 對于這個出度序列中任意 $k$ 個元素,滿足他們的和 $\geq \cfrac{k(k-1)}{2}$ 。
由于我懶得寫證明(證明需要用構造法,自行百度),這個結論的證明略去。
于是我們隻需要保證最終的出度序列的總和為 $\cfrac{n(n-1)}{2}$ ,并且将其排序後,對于所有 $k \in [1,n]$ ,前 $k$ 個元素之和 $\geq \cfrac{k(k-1)}2$ 即可。
我們按照數值從小到大填。
我們令 $dp[i][j][k]$ 表示前 $i$ 個數,目前最後一個數為 $j-1$ ,前 $i$ 個數的總和為 $k$ 的方案總數。然後大力 DP 即可。
dp 複雜度的上限是 $O(n^5)$ 的,但是由于有很多無用的狀态,是以 20 組資料仍然可以跑過去。
代碼
#include <bits/stdc++.h>
using namespace std;
const int N=45,mod=1e9+7;
int T,n,a[N],C[N][N],dp[N][N][N*N],cnt[N],tot[N];
int calc(int x){
return x*(x-1)/2;
}
void add(int &x,int y){
x+=y;
if (x>=mod)
x-=mod;
}
int solve(){
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+n+1);
memset(cnt,0,sizeof cnt);
for (int i=1;i<=n;i++)
if (a[i]>=0)
cnt[a[i]]++;
memset(tot,0,sizeof tot);
for (int i=n;i>=0;i--)
tot[i]=tot[i+1]+cnt[i];
memset(dp,0,sizeof dp);
dp[0][0][0]=1;
for (int i=0;i<n;i++)
for (int j=0;j<n;j++)
for (int k=0;k<=calc(n);k++){
int v=dp[i][j][k];
if (!v)
continue;
for (int t=0;i+t<=n-tot[j+1];t++){
int _k=k+t*j;
if (_k>calc(n)||calc(i+t)>_k)
break;
if (t<cnt[j])
continue;
add(dp[i+t][j+1][_k],1LL*v*C[n-i-tot[j]][t-cnt[j]]%mod);
}
}
int ans=0;
for (int i=0;i<=n;i++)
add(ans,dp[n][i][calc(n)]);
return ans;
}
int main(){
for (int i=0;i<N;i++)
C[i][0]=C[i][i]=1;
for (int i=1;i<N;i++)
for (int j=1;j<i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
scanf("%d",&T);
while (T--)
printf("%d\n",solve());
return 0;
}
/*
dp[i][j][k]表示前 i 個數,最後一個數是 j , 所有數的總和為 k 的序列總數
dp[i+t][j+1][k+t*j]+=C[n-i][t]*dp[i][j][k]
j<n,i+t<=n
k+t*j<=n*(n-1)/2
forall t' in [0,t] , (i+t')*(i+t'-1)/2<=k+t*j
*/