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;
}

感谢观看!