[AtCoder Beginner Contest 281][G. Farthest City]

CF1657E 的做法十分相似

题目链接:G - Farthest City

题目大意:问有多少个 n(3n500) 个点的无向连通图满足,若设 1i 的最短路距离为 disi,则 disn 严格大于其它所有的 disi。输出方案数对 M(108M109) 取模。

考虑最短路的值一定是连续的,那么若最短路为 i 的点数有 ci 个,且 disn=k,则方案数可以计算

Ans=(n2c2,c3,...,ck1)×i=1k(2ci11)ci×2(ci2)

其中,i=0kci=n,c0=ck=1(2ci11)ci 的含义为最短路为 i 的点是从最短路为 i1 的点连接过来的,那么对于每个这样的点,他可以选择上一层的任意一个非空子集进行连接。2(ci2) 的含义则是,对当前层的所有点,两两之间可以任意连接,对应可连接的边数为 (ci2),于是会有 2(ci2) 的系数。

考虑背包,按最短路的值从小到大一批批安排当前点,那么有一个初始的 DP 想法,就是令 fi,j,k 表示当前已经安排了 i 个点,目前最短路的最大值为 j,对应点的个数为 k 的方案数,那么枚举最短路值为 j+1 的点的数量 cnt,可以转移到 fi+cnt,j+1,cnt,转移系数为 (n1icnt)×(2j1)cnt×2(cnt2)。但是直接这样做是 O(n4) 的。

发现转移系数与 j 无关,所以这一维可以直接省去,于是 DP 方式就变成,设 fi,j 表示当前已经安排了 i 个点,且目前最短路最大的点数为 j 的方案数,那么同样枚举下一层的点数 k,就能转移到 fi+k,k,转移系数为 (n1ik)×(2j1)k×2(k2)。初始值设 f1,1=1,最后答案即为 j=1n1fn1,j×(2j1)

#include<bits/stdc++.h>
using namespace std;
#define N 510
#define LL long long
int n,MOD,f[N][N],c[N][N],p[N*N],ans;
LL qow(LL x,LL y){return y?(y&1?x*qow(x,y-1)%MOD:qow(x*x%MOD,y/2)):1;}
int main()
{
	scanf("%d%d",&n,&MOD);
	p[0]=c[0][0]=1;
	for(int i=1;i<=n;i++){
		c[i][0]=c[i][i]=1;
		for(int j=1;j<i;j++)
			c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD;
	}
	for(int i=1;i<=n*n;i++)p[i]=2ll*p[i-1]%MOD;
	f[1][1]=1;
	for(int i=1;i<n;i++)
	for(int j=1;j<=i;j++)if(f[i][j])
		for(int k=1;i+k<n;k++)
			f[i+k][k]=(f[i+k][k]+1ll*f[i][j]*c[n-i-1][k]%MOD*qow(p[j]+MOD-1,k)%MOD*p[k*(k-1)/2]%MOD)%MOD;
	for(int j=1;j<=n-1;j++)
		ans=(ans+1ll*f[n-1][j]*(p[j]+MOD-1)%MOD)%MOD;
	printf("%d\n",ans);
}

时间复杂度为 O(n3logn),预处理了 2i 的值稍微进行了点常数优化。实际上是可以通过 O(n2) 预处理每个 (2j1)k 的值把复杂度降到 O(n3) 的,但是既然能过就不优化了。╮(╯▽╰)╭

posted @   DeaphetS  阅读(41)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示