视频加载失败

安吉D12-T2

2290 字
11 分钟
安吉D12-T2
原题呈现

森林基站#

题目描述#

熊大为了保卫狗熊岭,要在森林里建立一个由 nn 个通信基站组成的预警网络.这 nn 个基站由 n1n-1 条隐蔽的光缆连接,形成了一棵树的结构.

为了保证信号畅通,熊大需要为每个基站分配一个运行频率.频率可以选择 1m1 \sim m 之间的任意整数.

然而,频率 kk 是一个“高功率特殊频率”.由于高功率频率之间会互相干扰,且对周围低功率设备有压制作用,光头强的探测器很容易发现异常.为了安全,熊大设定了以下规则:

  1. 整个网络中,最多只能有 xx 个基站被分配为频率 kk
  2. 如果某个基站被分配了频率 kk,那么与它通过光缆直接相连的所有相邻基站的频率必须严格小于 kk

熊大想知道,一共有多少种合法的频率分配方案?两种方案被认为是不同的,当且仅当至少有一个基站被分配了不同的频率.

由于答案可能很大,请将结果对 998244353998244353 取模后告诉熊大.

输入格式#

输入文件名为 forest.in

输入的第一行包含一个整数 TT,表示测试数据的组数.

对于每组测试数据:

  • 第一行包含四个整数 n,m,k,xn, m, k, x,分别表示基站的数量、可选频率的上限、高功率特殊频率的值,以及最多允许分配频率 kk 的基站数量.

  • 接下来 n1n-1 行,每行包含两个整数 u,vu, v,表示基站 uu 和基站 vv 之间有一条光缆相连.

输出格式#

输出文件名为 forest.out

对于每组测试数据,输出一行一个整数,表示合法的频率分配方案数对 998244353998244353 取模后的结果.

样例#

输入 #1#

2
3 3 2 1
1 2
2 3
4 3 2 2
1 2
1 3
1 4

输出 #1#

13
35

样例解释#

【样例 1 解释】

第 1 组测试数据: 有 33 个基站,频率可以选择 1,2,31, 2, 3.高功率特殊频率是 22,最多允许有 11 个基站使用频率 22

  • 不使用频率 22:每个基站都可以选择频率 1133,共有 23=82^3 = 8 种方案.
  • 恰好有 11 个基站使用频率 22
    • 若基站 11 频率为 22,则相邻的基站 22 频率必须严格小于 22(只能为 11),基站 33 的频率可以是 1133,共 22 种方案.
    • 若基站 22 频率为 22,则相邻的基站 1133 频率都必须为 11,共 11 种方案.
    • 若基站 33 频率为 22,则相邻的基站 22 频率必须为 11,基站 11 的频率可以是 1133,共 22 种方案.

合计共有 8+2+1+2=138+2+1+2 = 13 种合法的分配方案.

第 2 组测试数据: 有 44 个基站,结构为以 11 为中心的菊花图.频率可选 1,2,31,2,3,特殊频率是 22,最多允许 22 个基站使用频率 22

  • 若基站 11 使用频率 22,则基站 2,3,42,3,4 必须全部使用频率 11.共 11 种方案.
  • 若基站 11 不使用频率 22
    • 若基站 11 使用频率 11,则基站 2,3,42,3,4 可随意选择 1,2,31,2,3,但频率 22 的基站数不能超过 22.这部分共有 23+3×22+3×2=8+12+6=262^3 + 3 \times 2^2 + 3 \times 2 = 8 + 12 + 6 = 26 种方案.
    • 若基站 11 使用频率 33,则基站 2,3,42,3,4 不能使用频率 22(因为使用频率 22 的基站,其所有邻居的频率必须严格小于 22).所以基站 2,3,42,3,4 只能选择频率 1133,共 23=82^3 = 8 种方案.

总计 1+26+8=351 + 26 + 8 = 35 种方案.

数据范围#

对于所有测试数据,保证:

  • 1T101 \le T \le 10
  • 1n30001 \le n \le 3000
  • 1x30001 \le x \le 3000
  • 1km1091 \le k \le m \le 10^9
  • 1u,vn1 \le u, v \le n
  • 所有测试数据的 nn 之和不超过 1000010000
测试点编号nn \lemm \lexx \le特殊性质
1 ~ 210310
3 ~ 41210910^912
51510910^915
6 ~ 820010910^9200
9 ~ 11300010910^910
12 ~ 14300010910^93000A(菊花图)
15 ~ 17300010910^93000B(链)
18 ~ 20300010910^93000

特殊性质说明

  • A:树的形态为菊花图,即存在一个基站 cc,其余所有基站都与基站 cc 直接相连.
  • B:树的形态为一条链,即每个基站的度数均不超过 22

观察到 n3000n\leq 3000,可以支持 O(n2)O(n^2) 的做法.此题选用 dp.由于每一个节点的决策都只与总共存在的高功率节点和其相邻节点有关,因此,定义 dp[i][j][k] 表示考虑 ii 为根的子树,子树内已经有了 jj 个高功率节点(包括点 ii),由于根据不同情况,ii 也会有不同的取值,因此最后一维 k[0,2]k\in[0,2],表示节点 ii 频率 vv 的取值范围所拥有的方案数:

  • 1vk11\leq v\leq k-1,此时 k=0k=0
  • v=kv=k,此时 k=1k=1
  • k+1vmk+1\leq v\leq m,此时 k=2k=2

不难发现初始状态:考虑了 0 个节点,子树已有 0 个高功率节点,共有 1 种情况.根据树形背包的相关知识,即 g[0][0][0/1/2] = 1

接下来根据 kk 的不同分别考虑转移:

  • k=0k=0,则表示相邻节点无限制.因此,可以从其子节点 k{0,1,2}k\in\{0,1,2\} 的状态转移而来.具体来说,若 vv 是第 ii 个子节点:g[i][j+k][0]=g[i1][j][0]×(l=02dp[v][k][l])g[i][j+k][0] =g[i-1][j][0] \times (\sum_{l=0}^{2}dp[v][k][l]).注意其中是加号还是乘号.互斥的状态是相加,承接的状态是相乘.
  • k=1k=1,则其相邻节点只能取到 [1,k)[1,k).因此,可以从其子节点 k=0k=0 的状态转移而来.具体来说,若 vv 是第 ii 个子节点:g[i][j+k][1]=g[i1][j][1]×dp[v][k][0]g[i][j+k][1] =g[i-1][j][1] \times dp[v][k][0]
  • k=2k=2,其相邻节点不能是高功率节点.因此,可以从其子节点 k{0,2}k\in\{0,2\} 的状态转移而来.具体来说,若 vv 是第 ii 个子节点:g[i][j+k][2]=g[i1][j][2]×(dp[v][k][0]+dp[v][k][2])g[i][j+k][2] =g[i-1][j][2] \times (dp[v][k][0] + dp[v][k][2])

接下来就完全是树形背包的转移.

最终状态是 k=02dp[root][x][i]\sum_{k=0}^{2}dp[root][x][i]

小技巧:状态的定义与转移

状态的定义常和问题有关.极简单的状态定义就是根据问题定义状态.例如本题询问有多少种方案数,于是就可以定义 dp 状态为考虑到某个节点(或考虑完某节点的子树)时可行的方案数.

稍复杂的 dp 需要加一些限制条件.如此题,由于一个节点的决策只与其子节点决策和剩余“高功率节点”配额相关,于是需要在状态中增加两维.又由于父节点决策只与子节点决策的范围(与 kk 的大小关系)有关,因此可以坍缩一维大小为 3 的维度,使其成为常数维.需要注意的是,

  • 在增加限制时,不得依赖于当前节点的父节点.如,我定义一维状态表示当前节点有没有受到父节点的影响,这是不可行的
  • 状态不能重复、不能遗漏.如,我定义一维常数维,其中 0 表示当前节点不受影响的方案数(当前节点可以取 [1,m][1,m]),1 表示当前节点受影响的方案数(当前状态可以取 [1,k1][1,k-1]),这两者在 [1,k1][1,k-1] 的值域内重叠了.

此外,在转移时,特别是方案数,需要注意运算符号究竟是加号还是乘号.相互排斥的状态需要相加,相互承接的状态需要相乘.

标程

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3005;
const int MOD = 998244353;
int n, m, k, x, root;
vector<int> e[N];
int dp[N][N][3], g[2][N][3], sz[N];
void dfs(int u, int fa) {
sz[u] = 1;
for (int i = 0; i < e[u].size(); i++) {
int v = e[u][i];
if (v != fa) {
dfs(v, u);
sz[u] += sz[v];
}
}
if (u != root && e[u].size() == 1) {
dp[u][0][0] = k - 1;
dp[u][1][1] = 1;
dp[u][0][2] = m - k;
return;
}
g[0][0][0] = 1;
g[0][0][1] = 1;
g[0][0][2] = 1;
int totsz = 0;
int offset = 0;
for (int xi = 1; xi <= e[u].size(); xi++) {
int v = e[u][xi - 1];
if (v == fa) {
offset++;
continue;
}
int i = xi - offset;
int now = i & 1, prev = (i + 1) & 1;
memset(g[now], 0, sizeof(g[now]));
for (int j = 0; j <= totsz; j++) {
for (int k = 0; k <= sz[v]; k++) {
(g[now][j + k][0] +=
1ll * g[prev][j][0] *
(0ll + dp[v][k][0] + dp[v][k][1] + dp[v][k][2] % MOD) % MOD) %=
MOD;
(g[now][j + k][1] += 1ll * g[prev][j][1] * dp[v][k][0] % MOD) %=
MOD;
(g[now][j + k][2] += 1ll * g[prev][j][2] *
(0ll + dp[v][k][0] + dp[v][k][2] % MOD) %
MOD) %= MOD;
}
}
totsz += sz[v];
}
int maxs = (e[u].size() - offset) & 1;
for (int i = 0; i <= totsz; i++) {
(dp[u][i][0] = 1ll * g[maxs][i][0] * (k - 1) % MOD) %= MOD;
dp[u][i + 1][1] = g[maxs][i][1] % MOD;
dp[u][i][2] = 1ll * g[maxs][i][2] * (m - k) % MOD;
}
}
void solve() {
memset(sz, 0, sizeof(sz));
memset(dp, 0, sizeof(dp));
cin >> n >> m >> k >> x;
for (int i = 1; i <= n; i++) {
e[i].clear();
}
root = -1;
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
if (root == -1) {
root = u;
}
e[u].push_back(v);
e[v].push_back(u);
}
dfs(root, 0);
ll ans = 0;
for (int i = 0; i <= x; i++) {
(ans += dp[root][i][0]) %= MOD;
(ans += dp[root][i][1]) %= MOD;
(ans += dp[root][i][2]) %= MOD;
}
cout << ans << endl;
}
signed main() {
int T;
cin >> T;
while (T--)
solve();
return 0;
}

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

安吉D12-T2
https://blog.jerrylab.top/posts/problem/anji2026/D12/T2/
作者
Jerry
发布于
2026-08-12
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Jerry
Hello, I'm Jerry.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签
最新动态
站点统计
文章
80
分类
3
标签
35
总字数
89,198
运行时长
0
最后活动
0 天前
站点信息
构建平台
Cloudflare Pages
博客版本
Firefly v6.16.5
文章许可
CC BY-NC-SA 4.0