2020.11.16 比赛总结&题解合集

本来以为考的还行,结果大规模挂分…退役指日可待…

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+m maxai 左右,超出这个范围的概率很小。
考试的时候不知道学校的评测机速度,结果开小了一点点…
这种题目放在 OI 里面确实不是很合适,而且 T1 T2 时限都太紧了…

T2-树上竞技(meeting)

原题:HDU6810 Imperative Meeting

算法1:30%

n ≤ 25 n\leq 25 n25

考虑 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(nsizi)

算法3:+20%

树是一条链, m m m是奇数。

显然中心点就是 m + 1 2 \frac{m+1}{2} 2m+1 ,那么我们枚举这个中心点所在位置,对两边可能的位置排布用组合数简单计算一下即可。

算法4:+0%

n ≤ 1000 n\leq 1000 n1000

出题人忘记设置这档部分分了…
不管是树上的重心还是带权重心,它都有一个重要的性质,就是可定向性。
例如,对于一个点而言,如果重心不是它自己,那么重心一定是它的重儿子;对于一条边而言,重心一定在两边中点数更多的一部分内。
带权重心加个权就可以了。
那么对于一条边而言,我们只需要确定选中点在这条边的两边的分布就能够确定方向了。
假设一条边的一侧的权重为 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=1m1(is)(mins)min(mi,i)
这样我们就得到了一个 O ( n 2 ) O(n^2) O(n2) 的做法。

算法5:100%

n ≤ 1 0 6 n\leq 10^6 n106

我们延续 算法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=1pi(is)(mins)
其中 p = ⌊ m − 1 2 ⌋ p=\lfloor\frac{m-1}{2}\rfloor p=2m1
显然有
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(ns)+[mmod2=0]2m(2ms)(2mns)
注意到系数 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=1pi(is)(mins)=si=1p(i1s1)(mins)
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(i1s1)(mins),现在我们只需要求出这个式子的值即可。
这个式子一看就很具有现实意义:)实际上求 n − 1 n-1 n1 个位置,放 m − 1 m-1 m1 个球,每个位置最多能放一个球,在 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(s1)(s2p1)(nsm1p)
这样我们就可以 O ( n ) O(n) O(n) 求解了。

T3-虚构推理(unreal)

原题:HDU6807 Fake Photo

算法1:10%

n ≤ 2 n\leq 2 n2

考虑当时针在两时针之间的角平分线上时,分针也在其角平分线上。我们只需要讨论时针平分的是钝角还是锐角,两种情况取 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 n50000

其实想法很简单,就是写起来…
考虑二分答案,那么对于每个时针分针都会生成一个合法指针区间,我们对时针和分针的限制区间分别取交集。
如果时针范围大于 1 h 1h 1h ,那么分针显然可以走到任何地方;否则分针还会生成一个限制范围,我们再取交集即可。
复杂度: O ( n log ⁡ n log ⁡ − 1 ε ) O(n\log n\log^{-1} ε) O(nlognlog1ε)

T4-记忆碎片(tree)

又是一道不常规的状压题目,以后一定要注意。

算法1:10%

n ≤ 5 n\leq 5 n5

枚举每条边填上了哪个数字,然后暴力 check 即可。
复杂度: O ( m ! m log ⁡ m ) O(m!m\log m) O(m!mlogm)
m m m代表总边数)

算法2:30%

n ≤ 7 n\leq 7 n7(实际上是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 n15

其实在 算法2 中,我们的 dp 存储了一些多余的不必要的信息。事实上,在 算法2 转移的过程中,我们只利用了 “哪个点在哪个连通块中” 这样的信息。我们不必拘泥于使用二进制类型的状态,而是采用最小表示法存储状态。
这样做看上去比较悬,但是写一个搜索可以发现状态数还是很少的:)

算法4:+20%

a i ≤ n a_i\leq n ain

很有研究意义的一个部分,考虑如过我们用 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=3n2i!×(in)nni1
即我们枚举这个环大小为 i i i ,从 n n n 个标号中选 i i i 个标号放在环上,它们在环上排列的方案数为 i ! 2 \frac{i!}{2} 2i!。解决完这些之后,我们就可以把它缩成一个点,那么就变成了求 n − i + 1 n-i+1 ni+1 个点的有标号生成树计数,即 n n − i − 1 n^{n-i-1} nni1 种。
接下来我们在这之上安排边的标号,为了符合题意,设唯一不存在与生成树上的权值为 [ 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)! (mn)!i=3n2i!×(in)nni1×(i1p1)i!(ni)!
复杂度: O ( n ) O(n) O(n)
实际上这个部分分可以把数据范围开到 n ≤ 1 0 18 n\leq 10^{18} n1018 因为只有 n ≤ 998244353 n\leq \sqrt{998244353} n998244353 的答案不为零。

算法5:100%

n ≤ 40 n\leq 40 n40

我们进一步优化 算法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(n2n P(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


这次总结写多了,有点费时间,下次少写点…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值