题目
思路1
代码实现比较有技术,其实不长
时间复杂度是 O ( n P ( n ) ) O(nP(n)) O(nP(n)) 的,因为总共只有 P ( n ) P(n) P(n) 个状态
考试时间还是很紧,一定要多想T4部分分
代码1
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<climits>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
int read(){
bool f=0;int x=0;char c=getchar();
while(c<'0'||'9'<c){if(c=='-')f=1;c=getchar();}
while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return !f?x:-x;
}
#define mp make_pair
const int MAXN=1000;
const int MAXP=40000;
const int Mod=(int)(1e9+7);
int Mul(LL x,int y){x*=y;return x>=Mod?x%Mod:x;}
int Sub(int x,int y){x-=y;return x<0?x+Mod:x;}
int Add(int x,int y){x+=y;return x>=Mod?x-Mod:x;}
bool a[MAXN+5];
vector<int> nw,S[MAXP+5];
int tp,f[MAXN+5][MAXP+5],cnt[MAXP+5];
map<vector<int>,int> Map;//没用Hash有点慢
void Init(int s,int p){//拆分并记录内部边的数量
if(!p){
if(!s){
S[++tp]=nw;
Map[nw]=tp;
for(int i=0;i<(int)nw.size();i++)
cnt[tp]+=nw[i]*(nw[i]-1)/2;
}
return ;
}
Init(s,p-1);
if(s>=p){
nw.push_back(p);
Init(s-p,p);
nw.pop_back();
}
return ;
}
int main(){//f[i][S]:边[1,...,i]构成连通块大小集合为S的方案数
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
int n=read(),m=n*(n-1)/2;
for(int i=2;i<=n;i++)
a[read()]=1;
Init(n,n);
f[0][1]=1;
for(int i=1;i<=m;i++)
for(int j=1;j<=tp;j++){
if(!f[i-1][j]) continue;
int val=f[i-1][j];
if(a[i]){
vector<int> u=S[j];
for(int x=0;x<(int)u.size();x++)
for(int y=x+1;y<(int)u.size();y++){
int ad=Mul(val,Mul(u[x],u[y]));
vector<int> v;//进行合并
v.push_back(u[x]+u[y]);
for(int z=0;z<(int)u.size();z++)
if(z!=x&&z!=y)
v.push_back(u[z]);
sort(v.begin(),v.end(),greater<int>());
f[i][Map[v]]=Add(f[i][Map[v]],ad);
}
}
else f[i][j]=Add(f[i][j],Mul(val,(cnt[j]-(i-1))));
}
printf("%d\n",f[m][tp]);
return 0;
}
思路2
满足
a
i
≤
n
a_i\le n
ai≤n 的部分分:
我们可以想想做法,完全就是另外一道题目了
首先假设缺的是
x
x
x
那么答案是:
(
n
∗
(
n
−
1
)
2
−
n
)
!
∑
i
=
3
x
A
n
i
2
A
x
−
1
i
−
1
n
n
−
i
−
1
i
(
n
−
i
)
!
(\frac{n*(n-1)}{2}-n)!\sum_{i=3}^x\frac{A_n^i}{2}A^{i-1}_{x-1}n^{n-i-1}i(n-i)!
(2n∗(n−1)−n)!i=3∑x2AniAx−1i−1nn−i−1i(n−i)!
我们可以一步步推导
首先
[
1
,
x
−
1
]
,
[
x
+
1
,
n
]
[1,x-1],[x+1,n]
[1,x−1],[x+1,n] 必定是树边
模拟
k
r
u
s
k
a
l
kruskal
kruskal 算法可得
x
x
x 和
[
1
,
x
−
1
]
[1,x-1]
[1,x−1] 之间的边会组成环
枚举环的大小
i
i
i
将环从
x
x
x 这条边破开成为链
那么之间有
i
i
i 个点
i
−
1
i-1
i−1 条权值在
[
1
,
x
−
1
]
[1,x-1]
[1,x−1] 之间的边
方案数是
A
n
i
2
A
x
−
1
i
−
1
\frac{A_n^i}{2}A^{i-1}_{x-1}
2AniAx−1i−1
然后是一个环大小为
i
i
i 的基环树边之间无区别的计数
n
n
−
i
−
1
i
n^{n-i-1}i
nn−i−1i
如果这个结论不会可以点这里:
Prufer序列/Prufer编码
然后将剩下
n
−
i
n-i
n−i 条树边分配
然后将剩下的边分配即可
注意ksm负指数
后记
以后一定开启 -Wall 和 -Wshadow