QOJ7126. Control point 题解

题目链接

题意描述

给定一颗 \(n\) 个节点的树,有 \(m\) 个节点是特殊节点,现在你需要选择一些点作为控制节点,使得任意一个特殊节点都能通过树上的至多 \(r\) 条边,到达至少一个控制节点,输出选择控制节点的方案数。

解题思路

直接做不好做,在树上选择一些节点为控制节点,\(d_i\) 表示点 \(i\) 与控制节点的距离,把其转换为序列,就会发现,每一种填数位的方案都与一种序列一一对应,于是便好做了。

在树上选择一些节点为控制节点,\(d_i\) 表示 \(i\) 个点,与控制节点最近的距离,把其转换为序列,就会发现,每一种填数位的方案都与一种序列一一对应,于是便好做了。

我们考虑每个点 \(u\) 它可以有哪些控制点影响,无非就是从儿子来的,或者从父亲来的,知道了这些就可以考虑DP。

状态设计

设计这样的DP状态\(f[u][k][0/1]\),表示在节点 \(u\) 填 \(k\),\(0/1\),是从父亲/儿子转移过来。

记 \(lim[u]\) 为 \(u\) 节点最远的可以被覆盖的距离,对于特殊节点, \(lim[u]=r\),否则 \(lim[u] = n\)。

初始状态

  • \(f[u][0][1]=1\)
  • \(\forall i\in[1,lim[u]],dp[u][i][0]=1\)
  • 不合法的 \(f[u][i][0/1]=0\)

状态转移

\(g\) 是滚动数组

  • \(f[u][i][0] \gets g[i][0]\times(f[v][i-1][0]+f[v][i][1] + f[v][i+1][1])\)
  • \(f[u][i][1] \gets g[i][1]\times(f[v][i-1][0]+f[v][i-1][1]+f[v][i][1]+f[v][i+1][1])\)
  • \(f[u][i][1] \gets g[i][0]\times f[v][i-1][1]\)

答案统计

\(ans = \sum_{i=1}^nf[1][i][1]\)

code:

#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define out() (cout << "sb\n")

const int N = 2005, mod = 1e9 + 7;

int n, m, r, lim[N];
ll dp[N][N][2], g[N][2]; //dp[u][i][0/1]:当前位置 u,填的数是i,下一层是否有小于它的数(说明子树内有控制点) 
vector<int> G[N];

void dfs(int u, int p) {
	dp[u][0][1] = 1; //选做控制点,填0 
	for (int i = 1; i <= lim[u]; i++) dp[u][i][0] = 1; //不选 
	for (int &v : G[u]) {
		if (v == p) continue;
		dfs(v, u);
		for (int i = 0; i <= lim[u]; i++) 
			g[i][0] = dp[u][i][0], g[i][1] = dp[u][i][1];
		dp[u][0][1] = g[0][1] * (dp[v][0][1] + dp[v][1][0] + dp[v][1][1]) % mod;
		for (int i = 1; i <= lim[u]; i++) {
			dp[u][i][0] = g[i][0] * (dp[v][i][1] + dp[v][i + 1][1] + dp[v][i + 1][0]) % mod;
			dp[u][i][1] = g[i][1] * (dp[v][i - 1][1] + dp[v][i][1] + dp[v][i + 1][0] + dp[v][i + 1][1]);
			dp[u][i][1] = (dp[u][i][1] + g[i][0] * dp[v][i - 1][1]) % mod;
		}
	}
}

int main(){
	//system("fc .out .out");
	//freopen(".in", "r", stdin);
	//freopen(".out", "w", stdout);
	IOS;
	cin >> n >> m >> r;
	for (int i = 1; i <= n; i++) lim[i] = n;
	for (int i = 1; i <= m; i++) {
		int x;
		cin >> x;
		lim[x] = r;
	}
	for (int i = 1; i < n; i++) {
		int u, v;
		cin >> u >> v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs(1, 0);
	ll ans = (m == 0);
	for (int i = 0; i <= n; i++)
		ans = (ans + dp[1][i][1]) % mod;
	cout << ans << "\n";
	return 0;
}



posted @ 2026-10-20 11:33  lcx_OIer  阅读(2)  评论(0)    收藏  举报