本来以为考的还行,结果大规模挂分…退役指日可待…
Part 1 总结
期望得分:
100
+
60
+
30
+
[
20
,
30
]
=
[
205
,
220
]
100+60+30+[20,30]=[205,220]
100+60+30+[20,30]=[205,220]。
实际得分:
30
+
50
+
55
+
10
=
145
30+50+55+10=145
30+50+55+10=145。
修正得分:
100
+
50
+
55
+
30
=
235
100+50+55+30=235
100+50+55+30=235。
T1 害怕 TLE 把数组开小到了
2
×
1
0
4
2\times 10^4
2×104 挂了 70 分。本质上其实是我不相信平时评测的速度。
T2 挂的 10 分的原因其实是因为出题人把数据开的太大了,
2
×
1
0
8
2\times10^8
2×108 在普通电脑上
1
s
1s
1s 基本上不可能跑过…
T3 数据有亿点水,多了 25 分。
T4 脑袋有包…模数用成了
998244353
998244353
998244353,挂了
20
20
20 分…现已加入暴毙滚粗豪华套餐。
本次挂分:
70
+
10
+
0
+
20
=
100
70+10+0+20=100
70+10+0+20=100,好久没挂过这么多分了…
Part 2 题解
tip:如果想看题面请跳到 Part 4。
T1-接力比赛(game)
原题:HDU6804 Contest of Rope Pulling
其实之前做过这道题目,当时也是我乱搞的这种做法…
直接做的话复杂度会是
O
(
n
2
max
a
i
)
O(n^2\max a_i)
O(n2maxai) 的,考虑将序列 random_shuffle
一下,我们就可以大胆的将背包容量缩小到
n
+
m
max
a
i
\sqrt{n+m}\max a_i
n+mmaxai 左右,超出这个范围的概率很小。
考试的时候不知道学校的评测机速度,结果开小了一点点…
这种题目放在 OI 里面确实不是很合适,而且 T1 T2 时限都太紧了…
T2-树上竞技(meeting)
原题:HDU6810 Imperative Meeting
算法1:30%
n ≤ 25 n\leq 25 n≤25
考虑 O ( ( n m ) ) O(\binom{n}{m}) O((mn)) 枚举选择了那些数,然后我们再用换根法 O ( n ) O(n) O(n) 求得带权重心。
算法2:+10%
m = 1 m=1 m=1
答案显然就是 ∑ i = 1 n s i z i ( n − s i z i ) \sum^n_{i=1}siz_i(n-siz_i) ∑i=1nsizi(n−sizi)。
算法3:+20%
树是一条链, m m m是奇数。
显然中心点就是 m + 1 2 \frac{m+1}{2} 2m+1 ,那么我们枚举这个中心点所在位置,对两边可能的位置排布用组合数简单计算一下即可。
算法4:+0%
n ≤ 1000 n\leq 1000 n≤1000
出题人忘记设置这档部分分了…
不管是树上的重心还是带权重心,它都有一个重要的性质,就是可定向性。
例如,对于一个点而言,如果重心不是它自己,那么重心一定是它的重儿子;对于一条边而言,重心一定在两边中点数更多的一部分内。
带权重心加个权就可以了。
那么对于一条边而言,我们只需要确定选中点在这条边的两边的分布就能够确定方向了。
假设一条边的一侧的权重为
s
s
s,我们可以轻松的得到这条边对答案的贡献:
f
(
s
)
=
∑
i
=
1
m
−
1
(
s
i
)
(
n
−
s
m
−
i
)
min
(
m
−
i
,
i
)
f(s)=\sum^{m-1}_{i=1}\binom{s}{i}\binom{n-s}{m-i}\min(m-i,i)
f(s)=i=1∑m−1(is)(m−in−s)min(m−i,i)
这样我们就得到了一个
O
(
n
2
)
O(n^2)
O(n2) 的做法。
算法5:100%
n ≤ 1 0 6 n\leq 10^6 n≤106
我们延续 算法4 的思路,尝试优化这个式子。
首先拆掉
min
\min
min,记:
g
(
s
)
=
∑
i
=
1
p
i
(
s
i
)
(
n
−
s
m
−
i
)
g(s)=\sum^{p}_{i=1}i\binom{s}{i}\binom{n-s}{m-i}
g(s)=i=1∑pi(is)(m−in−s)
其中
p
=
⌊
m
−
1
2
⌋
p=\lfloor\frac{m-1}{2}\rfloor
p=⌊2m−1⌋。
显然有
f
(
s
)
=
g
(
s
)
+
g
(
n
−
s
)
+
[
m
m
o
d
2
=
0
]
m
2
(
s
m
2
)
(
n
−
s
m
2
)
f(s)=g(s)+g(n-s)+[m\bmod 2=0]\frac{m}{2}\binom{s}{\frac{m}{2}}\binom{n-s}{\frac{m}{2}}
f(s)=g(s)+g(n−s)+[mmod2=0]2m(2ms)(2mn−s)
注意到系数
i
i
i和组合数内部可以约分,因此对
g
(
s
)
g(s)
g(s) 化简:
g
(
s
)
=
∑
i
=
1
p
i
(
s
i
)
(
n
−
s
m
−
i
)
=
s
∑
i
=
1
p
(
s
−
1
i
−
1
)
(
n
−
s
m
−
i
)
g(s)=\sum^{p}_{i=1}i\binom{s}{i}\binom{n-s}{m-i}\\ =s\sum^{p}_{i=1}\binom{s-1}{i-1}\binom{n-s}{m-i}\\
g(s)=i=1∑pi(is)(m−in−s)=si=1∑p(i−1s−1)(m−in−s)
设
h
(
s
)
=
∑
i
=
1
p
(
s
−
1
i
−
1
)
(
n
−
s
m
−
i
)
h(s)=\sum^{p}_{i=1}\binom{s-1}{i-1}\binom{n-s}{m-i}
h(s)=∑i=1p(i−1s−1)(m−in−s),现在我们只需要求出这个式子的值即可。
这个式子一看就很具有现实意义:)实际上求
n
−
1
n-1
n−1 个位置,放
m
−
1
m-1
m−1 个球,每个位置最多能放一个球,在
s
s
s 前最多放
k
k
k 个球的方案数。那么随着
s
s
s 的增加,很容易求得改变量为:
h
(
s
)
=
h
(
s
−
1
)
−
(
p
−
1
s
−
2
)
(
m
−
1
−
p
n
−
s
)
h(s)=h(s-1)-\binom{p-1}{s-2}\binom{m-1-p}{n-s}
h(s)=h(s−1)−(s−2p−1)(n−sm−1−p)
这样我们就可以
O
(
n
)
O(n)
O(n) 求解了。
T3-虚构推理(unreal)
原题:HDU6807 Fake Photo
算法1:10%
n ≤ 2 n\leq 2 n≤2
考虑当时针在两时针之间的角平分线上时,分针也在其角平分线上。我们只需要讨论时针平分的是钝角还是锐角,两种情况取 min \min min 即可。
算法2:+20%
对于任意 i , j i,j i,j 都满足 h i = h j h_i=h_j hi=hj 且保证所有 m i < 30 m_i<30 mi<30。
很容易看出此时一定是分针决定了最大角度,那么答案就是分针两两之间的最大夹角的一半。
算法3:100%
n ≤ 50000 n≤50000 n≤50000
其实想法很简单,就是写起来…
考虑二分答案,那么对于每个时针分针都会生成一个合法指针区间,我们对时针和分针的限制区间分别取交集。
如果时针范围大于
1
h
1h
1h ,那么分针显然可以走到任何地方;否则分针还会生成一个限制范围,我们再取交集即可。
复杂度:
O
(
n
log
n
log
−
1
ε
)
O(n\log n\log^{-1} ε)
O(nlognlog−1ε)。
T4-记忆碎片(tree)
又是一道不常规的状压题目,以后一定要注意。
算法1:10%
n ≤ 5 n\leq 5 n≤5
枚举每条边填上了哪个数字,然后暴力 check
即可。
复杂度:
O
(
m
!
m
log
m
)
O(m!m\log m)
O(m!mlogm)
(
m
m
m代表总边数)
算法2:30%
n ≤ 7 n\leq 7 n≤7(实际上是30分)
考虑对边进行状压 dp,转移模仿 kruskal 的过程。
定义
f
S
f_S
fS 表示从小到大确定边权,当前已经确定了边权的边的边集是
S
S
S 的方案数。
每次转移我们可以先通过边集合
S
S
S 还原现在的联通情况。如果当前边权被要求在最小生成树中,我们就只能连两端连通块不同的边,否则就只能连两端连通块相同的边。
复杂度:
O
(
2
m
m
)
。
O(2^mm)。
O(2mm)。
算法3:50%
n ≤ 15 n\leq 15 n≤15
其实在 算法2 中,我们的 dp 存储了一些多余的不必要的信息。事实上,在 算法2 转移的过程中,我们只利用了 “哪个点在哪个连通块中” 这样的信息。我们不必拘泥于使用二进制类型的状态,而是采用最小表示法存储状态。
这样做看上去比较悬,但是写一个搜索可以发现状态数还是很少的:)
算法4:+20%
a i ≤ n a_i\leq n ai≤n
很有研究意义的一个部分,考虑如过我们用 kruskal 来求最小生成树的话,我们会在加完权值为
[
1
,
n
]
[1,n]
[1,n] 的边之后求出这个生成树。那么显然这颗树是一个基环树。
我们首先来回忆一下
n
n
n 个点有标号基环树的求法:
∑
i
=
3
n
i
!
2
×
(
n
i
)
n
n
−
i
−
1
\sum_{i = 3} ^ n \frac{i!}{2}\times \binom{n}{i} n ^ {n-i-1}
i=3∑n2i!×(in)nn−i−1
即我们枚举这个环大小为
i
i
i ,从
n
n
n 个标号中选
i
i
i 个标号放在环上,它们在环上排列的方案数为
i
!
2
\frac{i!}{2}
2i!。解决完这些之后,我们就可以把它缩成一个点,那么就变成了求
n
−
i
+
1
n-i+1
n−i+1 个点的有标号生成树计数,即
n
n
−
i
−
1
n^{n-i-1}
nn−i−1 种。
接下来我们在这之上安排边的标号,为了符合题意,设唯一不存在与生成树上的权值为
[
1
,
n
]
[1,n]
[1,n] 的边为
p
p
p,那么它一定会在环上,且环上所有边权都应该比它小。对于权值大于
n
n
n 的边,随便放就可以了。
因此最终答案就是:
(
m
−
n
)
!
∑
i
=
3
n
i
!
2
×
(
n
i
)
n
n
−
i
−
1
×
(
p
−
1
i
−
1
)
i
!
(
n
−
i
)
!
(m-n)!\sum_{i = 3} ^ n \frac{i!}{2}\times \binom{n}{i} n ^ {n-i-1}\times \binom{p-1}{i-1}i!(n-i)!
(m−n)!i=3∑n2i!×(in)nn−i−1×(i−1p−1)i!(n−i)!
复杂度:
O
(
n
)
O(n)
O(n)。
实际上这个部分分可以把数据范围开到
n
≤
1
0
18
n\leq 10^{18}
n≤1018 因为只有
n
≤
998244353
n\leq \sqrt{998244353}
n≤998244353 的答案不为零。
算法5:100%
n ≤ 40 n\leq 40 n≤40
我们进一步优化 算法3 的想法,由于最小生成树只有边权组成要求,我们可以发现我们其实连每个点属于哪个联通块都可以不需要。
考虑状态变为连通块大小分布情况,而这个状态数就是分拆数
P
(
n
)
P(n)
P(n),而转移时,我们只需要对树边转移,且大小不同的连通块个数为
O
(
n
)
O(\sqrt{n})
O(n) 在转移时我们还需要再
O
(
n
)
O(\sqrt{n})
O(n) 求出下一个状态。
因此最好能够做到理论复杂度
O
(
n
2
n
P
(
n
)
)
O(n^2\sqrt{n}P(n))
O(n2nP(n))的解法(
P
(
40
)
=
37338
P(40)=37338
P(40)=37338),实际常数小,可以通过。
实际上我们的标程实现出来的理论复杂度远远大于这个复杂度,但是它就是过得了:)
这题在实现上技巧很多,至少我是看了std的…
Part 3 实现
T1-game
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<cstring>
using namespace std;
#define LL long long
#define DB double
#define Pr pair<int,int>
#define X first
#define Y second
#define MAXN 2000
#define MAXM 40000
#define INF 10000000000000000
#define MOD 998244353
#define mem(x,v) memset(x,v,sizeof(x))
LL read(){
LL x=0,F=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*F;
}
int n,m;
LL f[MAXM*2+5];
Pr a[MAXN+5];
int main(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=n;i++)a[i].X=read(),a[i].Y=read();
for(int i=1;i<=m;i++)a[n+i].X=-read(),a[n+i].Y=read();
random_shuffle(a+1,a+n+m+1);
for(int j=0;j<=MAXM*2;j++)f[j]=-INF;
f[MAXM]=0;
for(int i=1;i<=n+m;i++){
if(a[i].X>=0){
for(int j=MAXM*2;j>=0;j--)
if(j+a[i].X<=MAXM*2)f[j+a[i].X]=max(f[j+a[i].X],f[j]+a[i].Y);
}
else{
for(int j=0;j<=MAXM*2;j++)
if(j+a[i].X>=0)f[j+a[i].X]=max(f[j+a[i].X],f[j]+a[i].Y);
}
}
printf("%lld\n",f[MAXM]);
}
T2-meeting
算法1+算法2+算法3 实现
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<cstring>
using namespace std;
#define LL long long
#define DB double
#define Pr pair<int,int>
#define X first
#define Y second
#define MAXN 2000000
#define INF 10000000000000000
#define MOD 1000000007
#define mem(x,v) memset(x,v,sizeof(x))
LL read(){
LL x=0,F=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*F;
}
int add(int a,int b){return (a+b>=MOD)?a+b-MOD:a+b;}
int dec(int a,int b){return (a-b<0)?a-b+MOD:a-b;}
int mul(int a,int b){return (1LL*a*b)%MOD;}
int fst_pow(int a,int b){
int res=1;
while(b){
if(b&1)res=mul(res,a);
a=mul(a,a),b>>=1;
}return res;
}
int n,m,tag[MAXN+5],du[MAXN+5],ans,cnt,p;
int dep[MAXN+5],siz[MAXN+5];LL res,tmp;
vector<int> G[MAXN+5];
int fac[MAXN+5],ifac[MAXN+5],f[MAXN+5];
void dfs1(int x,int fa){
dep[x]=dep[fa]+1,siz[x]=0;
if(tag[x])tmp=tmp+dep[x],siz[x]=1;
for(int i=0;i<G[x].size();i++){
int v=G[x][i];
if(v==fa)continue;
dfs1(v,x);
siz[x]+=siz[v];
}
}
void dfs2(int x,int fa){
res=min(res,tmp);
for(int i=0;i<G[x].size();i++){
int v=G[x][i];
if(v==fa)continue;
tmp+=m-2*siz[v];
dfs2(v,x);
tmp-=m-2*siz[v];
}
}
void dfs3(int x,int fa){
siz[x]=1;
for(int i=0;i<G[x].size();i++){
int v=G[x][i];
if(v==fa)continue;
dfs3(v,x);
siz[x]+=siz[v];
}
ans=add(ans,mul(siz[x],n-siz[x]));
}
LL calc(){
dep[0]=-1;
res=INF,tmp=0,dfs1(1,0);
dfs2(1,0);
return res;
}
void solve(int x,int cnt){
if(cnt>m||cnt+n-x+1<m)return ;
if(x==n+1){
if(cnt==m)ans=add(ans,calc()%MOD);
return ;
}
tag[x]=1,solve(x+1,cnt+1);
tag[x]=0,solve(x+1,cnt);
}
void prepare(){
fac[0]=1;
for(int i=1;i<=MAXN;i++)fac[i]=mul(fac[i-1],i);
ifac[MAXN]=fst_pow(fac[MAXN],MOD-2);
for(int i=MAXN;i>=1;i--)ifac[i-1]=mul(ifac[i],i);
}
int Comb(int a,int b){
if(a<b)return 0;
return mul(fac[a],mul(ifac[b],ifac[a-b]));
}
int main(){
freopen("meeting.in","r",stdin);
freopen("meeting.out","w",stdout);
prepare();
n=read(),m=read();
for(int i=2;i<=n;i++){
int f=read();
G[f].push_back(i);
G[i].push_back(f);
du[f]++,du[i]++;
}
for(int i=1;i<=n;i++)
if(du[i]==1)cnt++,p=i;
if(m==1)puts("0");
else if(m==2){
dfs3(1,0);
printf("%d\n",ans);
}else if(cnt==2&&m%2==1){
m/=2;
for(int i=m;i<=n;i++)f[i]=mul(Comb(i-1,m-1),(1LL*i*(i+1)/2)%MOD);
for(int i=m+1;i<=n-m;i++)ans=add(ans,add(mul(f[i-1],Comb(n-i,m)),mul(f[n-i],Comb(i-1,m))));
printf("%d\n",ans);
}else{
solve(1,0);
printf("%d\n",ans);
}
}
算法4 实现
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<cstring>
using namespace std;
#define LL long long
#define DB double
#define Pr pair<int,int>
#define X first
#define Y second
#define MAXN 2000000
#define INF 10000000000000000
#define MOD 1000000007
#define mem(x,v) memset(x,v,sizeof(x))
LL read(){
LL x=0,F=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*F;
}
int add(int a,int b){return (a+b>=MOD)?a+b-MOD:a+b;}
int dec(int a,int b){return (a-b<0)?a-b+MOD:a-b;}
int mul(int a,int b){return (1LL*a*b)%MOD;}
int fst_pow(int a,int b){
int res=1;
while(b){
if(b&1)res=mul(res,a);
a=mul(a,a),b>>=1;
}return res;
}
int n,m,ans,p,siz[MAXN+5];
vector<int> G[MAXN+5];
int fac[MAXN+5],ifac[MAXN+5],f[MAXN+5],h[MAXN+5];
void prepare(){
fac[0]=1;
for(int i=1;i<=MAXN;i++)fac[i]=mul(fac[i-1],i);
ifac[MAXN]=fst_pow(fac[MAXN],MOD-2);
for(int i=MAXN;i>=1;i--)ifac[i-1]=mul(ifac[i],i);
}
int Comb(int a,int b){
if(a<b||a<0||b<0)return 0;
return mul(fac[a],mul(ifac[b],ifac[a-b]));
}
void dfs(int x){
siz[x]=1;
for(int i=0;i<G[x].size();i++){
int v=G[x][i];
dfs(v);
ans=add(ans,f[siz[v]]);
siz[x]+=siz[v];
}
}
int main(){
freopen("meeting.in","r",stdin);
freopen("meeting.out","w",stdout);
prepare();
n=read(),m=read();
for(int i=2;i<=n;i++){
int f=read();
G[f].push_back(i);
}
p=(m-1)/2,h[1]=Comb(n-1,m-1);
if(p==0)h[1]=0;//***
for(int i=2;i<n;i++)
h[i]=dec(h[i-1],mul(Comb(i-2,p-1),Comb(n-i,m-1-p)));
for(int i=1;i<=n;i++){
f[i]=add(mul(i,h[i]),mul(n-i,h[n-i]));
if(m%2==0)f[i]=add(f[i],mul(m/2,mul(Comb(i,m/2),Comb(n-i,m/2))));
}
dfs(1);
printf("%d\n",ans);
}
T3-unreal
算法1+算法2 实现
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<cstring>
using namespace std;
#define LL long long
#define DB double
#define Pr pair<int,int>
#define X first
#define Y second
#define MAXN 100000
#define MAXM 21
#define INF 1000000000
#define MOD 998244353
#define mem(x,v) memset(x,v,sizeof(x));
LL read(){
LL x=0,F=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*F;
}
int n;DB mx,mn,ans;
DB pabs(DB x){return (x<0)?x+360:x;}
int main(){
freopen("unreal.in","r",stdin);
freopen("unreal.out","w",stdout);
n=read();
if(n==2){
int h1=read(),m1=read(),s1=read();
if(h1>=12)h1-=12;
DB a1=30.0*h1+0.5*m1,b1=6.0*m1;
int h2=read(),m2=read(),s2=read();
if(h2>=12)h2-=12;
DB a2=30.0*h2+0.5*m2,b2=6.0*m2;
DB A1=pabs(a1-a2),B1=pabs(b1-b2);
DB A2=pabs(a2-a1),B2=pabs(b2-b1);
printf("%.5f",min(max(A1/2,B1/2),max(A2/2,B2/2)));
}else{
mn=180;
for(int i=1;i<=n;i++){
int h=read(),m=read(),s=read();
mx=max(mx,6.0*m+s*0.1);
mn=min(mn,6.0*m+s*0.1);
}
printf("%.5f",(mx-mn)/2.0);
}
}
算法3 实现
T4-tree
算法2+算法4 实现
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<cstring>
using namespace std;
#define LL long long
#define DB double
#define Pr pair<int,int>
#define X first
#define Y second
#define MAXN 100000
#define MAXM 21
#define INF 1000000000
#define MOD 1000000007
#define mem(x,v) memset(x,v,sizeof(x));
LL read(){
LL x=0,F=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*F;
}
int add(int a,int b){return (a+b>=MOD)?a+b-MOD:a+b;}
int dec(int a,int b){return (a-b<0)?a-b+MOD:a-b;}
int mul(int a,int b){return (1LL*a*b)%MOD;}
int fst_pow(int a,int b){
int res=1;
while(b){
if(b&1)res=mul(res,a);
a=mul(a,a),b>>=1;
}return res;
}
void upd(int &a,int b){a=add(a,b);}
int n,m,tot,a[MAXN+5],mx,vis[MAXN+5],ans,id,inv2;
int dp[(1<<MAXM)+5],fac[MAXN+5],ifac[MAXN+5],fa[MAXN+5];
Pr e[MAXN+5];
int xfind(int x){return (fa[x]==x)?x:fa[x]=xfind(fa[x]);}
int lowbit(int x){return x&(-x);}
int calc(int S){
int res=0;
while(S)res++,S-=lowbit(S);
return res;
}
void prepare(){
fac[0]=1;
for(int i=1;i<=m;i++)fac[i]=mul(fac[i-1],i);
ifac[m]=fst_pow(fac[m],MOD-2);
for(int i=m;i>=1;i--)ifac[i-1]=mul(ifac[i],i);
}
int Comb(int a,int b){
if(a<b)return 0;
return mul(fac[a],mul(ifac[b],ifac[a-b]));
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=read(),m=n*(n-1)/2;
for(int i=1;i<n;i++){
a[i]=read(),mx=max(mx,a[i]);
vis[a[i]]=1;
}
prepare();
if(mx==n-1){
printf("%d\n",mul(fst_pow(n,n-2),mul(fac[n-1],fac[m-(n-1)])));
return 0;
}
if(mx==n){
inv2=(MOD+1)>>1;
for(int i=1;i<=n;i++)
if(!vis[i])id=i;
for(int i=3;i<=n;i++){
int tot;
if(i==n)tot=mul(fac[n-1],inv2);
else tot=mul(Comb(n,i),mul(mul(fac[i],inv2),fst_pow(n,n-i-1)));
tot=mul(mul(tot,mul(Comb(id-1,i-1),fac[i])),mul(fac[n-i],fac[m-n]));
ans=add(ans,tot);
}
printf("%d\n",ans);
return 0;
}
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
e[++tot]=Pr(i,j);
dp[0]=1;
for(int S=0;S<(1<<m);S++){
int rnk=calc(S)+1;
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=0;i<m;i++)
if((S>>i)&1){
int a=xfind(e[i+1].X),b=xfind(e[i+1].Y);
if(a!=b)fa[a]=b;
}
for(int i=0;i<m;i++)
if(!((S>>i)&1)){
int a=xfind(e[i+1].X),b=xfind(e[i+1].Y);
if(vis[rnk]&&a!=b)upd(dp[S|(1<<i)],dp[S]);
if(!vis[rnk]&&a==b)upd(dp[S|(1<<i)],dp[S]);
}
}
printf("%d",dp[(1<<m)-1]);
}
算法5 实现
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<cstring>
using namespace std;
#define LL long long
#define DB double
#define Pr pair<int,int>
#define X first
#define Y second
#define MAXN 1000
#define MAXM 40000
#define INF 1000000000
#define MOD 1000000007
#define mem(x,v) memset(x,v,sizeof(x));
LL read(){
LL x=0,F=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*F;
}
int add(int a,int b){return (a+b>=MOD)?a+b-MOD:a+b;}
int dec(int a,int b){return (a-b<0)?a-b+MOD:a-b;}
int mul(int a,int b){return (1LL*a*b)%MOD;}
void upd(int &a,int b){a=add(a,b);}
int n,m,f[MAXN+5][MAXM+5],cnt[MAXM+5],id,vis[MAXN+5];
map<vector<int> , int>D;
vector<int> tmp,st[MAXM+5],nxt;
bool cmp(int a,int b){return a>b;}
void init(int x,int v){
if(v==0){
if(x==0){
D[tmp]=++id,st[id]=tmp;
for(int i=0;i<tmp.size();i++)cnt[id]+=tmp[i]*(tmp[i]-1)/2;
}
return ;
}
init(x,v-1);
if(x>=v){
tmp.push_back(v);
init(x-v,v);
tmp.pop_back();
}
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=read(),m=n*(n-1)/2;
for(int i=1;i<n;i++)vis[read()]=1;
init(n,n);
f[0][1]=1;
for(int i=1;i<=m;i++)
for(int j=1;j<=id;j++)
if(f[i-1][j]){
if(vis[i]){
tmp=st[j];
for(int a=0;a<tmp.size();a++)
for(int b=a+1;b<tmp.size();b++){
nxt.clear();
nxt.push_back(tmp[a]+tmp[b]);
for(int k=0;k<tmp.size();k++)
if(k!=a&&k!=b)nxt.push_back(tmp[k]);
sort(nxt.begin(),nxt.end(),cmp);
upd(f[i][D[nxt]],mul(f[i-1][j],mul(tmp[a],tmp[b])));
}
}else upd(f[i][j],mul(f[i-1][j],cnt[j]-i+1));
}
printf("%d",f[m][id]);
}
Part 4 resource
T1
T2
T3
T4
END
这次总结写多了,有点费时间,下次少写点…