0%

[ABC281G] Farthest City

一道很好的 dp 题。

题目链接

题意

现在有 个城市与一个数字

个城市之间用若干条无向边连接。并且任意两座城市互相可达。

并且满足对于任意城市 ,都有城市 到城市 的距离严格小于城市 到城市 的距离。

求总共有多少中连边的方案,对数字 取模。 可能不是质数。

题解

首先考虑一下符合条件的连边方式有什么特性。

想想就会发现,我们只要将所有点按照到 的距离进行“分层”,就能得到很好的性质。

个例子:

这张图符合条件,所有点到 的距离分别是

现在我们来分层。

发现什么了吗?

这场图不符合条件。所有点到 的距离分别是

现在我们来分层。

发现什么了吗?

我们可以发现满足条件的图都有这些性质:

  1. 在第 层;
  2. 在最后一层,并且最后一层只有点
  3. 对于每一条边,两端的点所在层数只差不超过 (也就是说,只可能存在同层之间连边,或相邻两层连边)。

条性质可能不太显然,但推一下就会发现,不可能存在一条边,两端点分别在第 层且

因为如果这样的话,在第 层的那个点最多应该在 层,不可能在 层。

有了这几条性质,我们就可以直接进行 dp 了。

我们首先设 个点形成了若干层,最后一层有 个点的连边方案数。

那么就有

其中,枚举 为倒数第二层有多少个节点。

表示上一层有 个点,下一层有 个点,这两层之间连边方式有多少种。

可以知道 ,其中 是下一层其中一个点的方案数(每一个点至少与上一层连一条边)。

表示同层之间连边方案数,显然每一条边连不连都一样。

表示从剩下的所有点中,选出 个点,指定为当前最后一层的点。

因为点 和点 位置已经固定,所以看起来比较奇怪。

边界条件为

最后只需要根据 的值算出答案即可。

时间复杂度为

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<map>
#define inf 0x3f3f3f3f3f3f3f3fll
using namespace std;
using ll=long long;
using pli=pair<ll,int>;
using pi=pair<int,int>;
template<typename A>
using vc=vector<A>;
inline int read()
{
int s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
ll C[501][501];
ll dp[501][501];
ll sum[501][501];
ll p[250001];
ll jc[501];
ll inv[501];
int n,mod;
ll ans;
inline ll qow(ll a,int b)
{
ll ans=1;
while(b)
{
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
inline void Add(ll &a,ll b)
{
a=(a+b)%mod;
}
int main()
{
n=read(),mod=read();
dp[1][1]=1;
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) sum[i][j]=qow(qow(2,j)-1,i);
jc[0]=p[0]=1;
for(int i=0;i<=n;i++)
{
if(i) jc[i]=jc[i-1]*i%mod;
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]=p[i-1]*2%mod;
for(int i=2;i<n;i++) for(int j=1;j<i;j++)
{
for(int k=1;k<=i-j;k++)
Add(dp[i][j],dp[i-j][k]*sum[j][k]%mod*C[n-1-(i-j)][j]%mod*p[j*(j-1)/2]);
}
for(int i=1;i<=n;i++) Add(ans,dp[n-1][i]*sum[1][i]);
printf("%lld\n",ans);
return 0;
}

感谢观看!