洛谷P3267.侦察守卫
题目大意
一颗 \(n(1\leq n\leq 5\times 10^5)\) 个节点的树,在某一点 \(i\) 花费 \(w_{i}(w_{i}\leq 1000)\) 放置一个侦察守卫后可以监视到所有到 \(i\) 的距离 \(\leq d(d\leq 20)\) 的点, 有 \(m(m\leq n)\)个给定的关键节点需要监视,求能够监视到全部 \(m\) 个节点所需要的最小花费。
思路
设 \(f[i,x]\) 为监视到以 \(i\) 为根的子树中的全部关键点,并且可以向上监视到距离不小于 \(i\) 的节点的最小花费; \(g[i,x]\) 为至少监视到了以 \(i\) 为根的子树中的全部的与 \(i\) 的距离 \(\geq x\) 的关键点所需要的最小花费。在合并子树的过程中,对于 \(i\) 的每一个儿子 \(j\) ,我们可以得到:
\[f[i,x]=min(f[i,x]+g[j,x],g[i,x+1]+f[j,x+1]) \]\[g[i,x]=g[i,x]+g[j,x-1] \]此外,由我们对状态的定义,显然有:
\[f[i,x]=min(f[i,x],f[i,x+1]) \]\[g[i,x]=min(g[i,x],g[i,x-1]) \]对于初始值,一开始先将所有 \(f\) 初始为 \(inf\) 。考虑到每一个子树一开始只有一个根节点 \(i\) ,于是,如果 \(i\) 是关键点, 那么 \(g[i,0]=f[i,0]=w_{i}\) ,否则, \(g[i,0]=f[i,0]=0\) ,因为此时 \(g\) 与 \(f\) 都恰好表示仅考虑节点 \(i\) 时的情况。如果 \(x\neq 0\) ,因为要向上监视,所以此处必须有一个侦察守卫,所以对于所有 \(1\leq x\leq d\) , \(f[i,x]=w_{i}\) 。
在每次与一个子树合并时,要先计算 \(f\) ,后计算 \(g\) 。因为 \(f\) 的转移方程中需要用到一个尚未与新的子树合并的 \(g\) 。此外由于 \(g[i,0]\) 不能通过转移方程得到,每次合并时计算完 \(f\) 后,需要令 \(g[i,0]=f[i,0]\) 。复杂度 \(O(nd)\) 。
代码
#include
#include
#include
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair PII;
#define all(x) x.begin(),x.end()
//#define int LL
//#define lc p*2+1
//#define rc p*2+2
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 500010;
vectorG[maxn];
int N, D, M, W[maxn];
bool key[maxn];
int f[maxn][25], g[maxn][25];
void add_edge(int from, int to)
{
G[from].push_back(to);
G[to].push_back(from);
}
void dfs(int v, int p)
{
if (key[v])
f[v][0] = g[v][0] = W[v];
else
f[v][0] = g[v][0] = 0;
for (int i = 1; i <= D; i++)
f[v][i] = W[v];
for (int i = 0; i < G[v].size(); i++)
{
int to = G[v][i];
if (to == p)
continue;
dfs(to, v);
for (int j = 0; j <= D; j++)
f[v][j] = min(f[v][j] + g[to][j], f[to][j + 1] + g[v][j + 1]);
for (int j = D; j >= 0; i--)
f[v][j] = min(f[v][j], f[v][j + 1]);
g[v][0] = f[v][0];
for (int j = 1; j <= D; j++)
g[v][j] += g[to][j - 1];
for (int j = 1; j <= D; j++)
g[v][j] = min(g[v][j], g[v][j - 1]);
}
}
void solve()
{
int ans = inf;
dfs(1, 0);
for (int i = 0; i <= D; i++)
ans = min(f[1][i], ans);
cout << ans << endl;
}
int main()
{
IOS;
memset(f, inf, sizeof(f));
cin >> N >> D;
for (int i = 1; i <= N; i++)
cin >> W[i];
cin >> M;
int b;
for (int i = 1; i <= M; i++)
{
cin >> b;
key[b] = true;
}
int u, v;
for (int i = 1; i < N; i++)
{
cin >> u >> v;
add_edge(u, v);
}
solve();
return 0;
}