题解:CF70E Information Reform
Lovya
·
·
题解
:::info[简要题意]{open}
给定一棵 n 个点的树,边长为 1。
选择若干个点,在点上建立 center,每个 center 的建立需要 k 元的花费。
对于每个点 i,如果点 i 上没有建立 center,则 l 表示点 i 与最近的 center 之间的距离,点 i 需要 d[l] 元的花费。
求一种建立 center 的方案,使得总花费最小。
:::
$F[v,s]$ 表示保证内部有一个 center 到点 $v$ 的距离 $\le s$ 时,花费的最小值。
$G[v,s]$ 表示保证外部有一个 center 到点 $v$ 的距离 $= s$ 时,花费的最小值。
(1)初始状态,$s=0$ 时,选择 $v$ 作为 center,花费 $k$ 元。且此时对于 $v$ 的子节点 $c$ 而言,到外部最近的 center 距离为 $1$,即 $G[c,1]$。
$$
F[v,0]=k+\sum_{c \in son(v)} G[c,1]
$$
(2)转移 $G[v,s]$:外部有一个 center 到点 $v$ 的距离 $= s$ 时,分情况讨论:
1. 内部有一个 center 到点 $v$ 的距离 $\le s$ 时,可以直接从内部的 center 转移,即 $F[v,s]$。
2. 从外部到点 $v$ 距离为 $s$ 的 center 转移。此时点 $v$ 需要 $d[s]$ 的花费,且对于 $v$ 的子节点 $c$ 而言,到外部的 center 距离为 $s+1$,即 $G[c,s+1]$。
$$
G[v,s]=\min
\begin{cases}
F[v,s]\\
d[s]+\sum_{c \in son(v)} G[c,s+1]
\end{cases}
$$
(3)转移 $F[v,s]$:内部存在一个 center 到点 $v$ 的距离 $\le s$ 时,分情况讨论:
1. 当内部存在一个 center 到点 $v$ 的距离 $\le s - 1$ 时,同时也就满足内部存在一个 center 到点 $v$ 的距离 $\le s$。因此可以从 $F[v,s-1]$ 转移。
2. 当内部不存在一个 center 到点 $v$ 的距离 $\le s - 1$ 时,则此时必然选择一个到点 $v$ 的距离 $= s$ 的 center 作为内部最近的 center,需要 $d[s]$ 的花费。同时,对于除了该到点 $v$ 的距离 $= s$ 的 center 的祖先 $c$ 外,其余点 $v$ 的子节点 $u$ 到该 center 的距离为 $s+1$,即从 $G[u,s+1]$ 转移。
$$
F[v,s]=\min
\begin{cases}
F[v,s-1]\\
\min_{c \in son(v)} d[s]+F[c,s-1]+\sum_{u \in son(v),u \not = c} G[u,s+1]
\end{cases}
$$
注意到,虽然 $F[c,s-1]$ 中选取的 center 未必到点 $v$ 的距离 $= s$,但 $< s$ 的情况已经被 $F[v,s-1]$ 的转移包含在内,两者取 $\min$,不影响结果。
最坏情况下,转移 $F[v,s]$ 的时间复杂度是 $\Omicron(n)$ 的。状态数最坏情况下是 $\Omicron(n^2)$ 的。$1 \le n \le 180$,可以通过。
答案即为 $\min_s F[root,s]$。
这道题还有一个问题,是需要输出使花费最小时,每个点 $i$ 所依靠的 center。
通过 BFS 计算任意两点 $i,j$ 之间的距离 $dist_{i,j}$。
通过回溯最小花费 $F[root,s]$ 的计算过程,确定所选取的 center,并记录每个点 $v$ 到其转移过程设计的最近 center 的距离 $Min_v$。
对于每个非 center 的点 $i$,依次枚举 center 点 $j$,若 $Min_i=dist_{i,j}$,则可以认为点 $j$ 是点 $i$ 所依靠的 center。
具体细节参见代码。
:::info[code]{open}
```cpp
#include<iostream>
#include<cstdio>
#include<queue>
#include<utility>
#include<cmath>
using namespace std;
int n,m,k,x,y,w_cnt,d[185],w_head[185],f[185][185],g[185][185],w_id[185],w_min[185],w_dist[185][185];
bool w_vis[185];
pair<int,int> w_now;
queue<pair<int,int> > q;
struct node{
int w_next,w_to;
}w_edge[365];
void w_add(int u,int v)
{
w_edge[++m]={w_head[u],v};
w_head[u]=m;
}
void w_dfs(int v,int w_fa)
{
f[v][0]=k;
for(int i=w_head[v];i;i=w_edge[i].w_next)
{
if(w_edge[i].w_to==w_fa)
continue;
w_dfs(w_edge[i].w_to,v);
f[v][0]+=g[w_edge[i].w_to][1];
}
// printf("f[%d][0]=%d\n",v,f[v][0]);
int w_temp=0;
for(int s=1;s<n;s++)
{
f[v][s]=f[v][s-1];
for(int i=w_head[v];i;i=w_edge[i].w_next)
{
if(w_edge[i].w_to==w_fa)
continue;
w_temp=d[s]+f[w_edge[i].w_to][s-1];
for(int j=w_head[v];j;j=w_edge[j].w_next)
{
if(w_edge[j].w_to==w_fa||w_edge[j].w_to==w_edge[i].w_to)
continue;
if(g[w_edge[j].w_to][s+1]==1e9)
w_temp=1e9;
else
w_temp+=g[w_edge[j].w_to][s+1];
}
f[v][s]=min(f[v][s],w_temp);
}
g[v][s]=f[v][s];
w_temp=0;
for(int i=w_head[v];i;i=w_edge[i].w_next)
{
if(w_edge[i].w_to==w_fa)
continue;
if(g[w_edge[i].w_to][s+1]==1e9)
w_temp=1e9;
else
w_temp+=g[w_edge[i].w_to][s+1];
}
g[v][s]=min(g[v][s],w_temp+d[s]);
// printf("f[%d][%d]=%d,g[%d][%d]=%d\n",v,s,f[v][s],v,s,g[v][s]);
}
f[v][n]=g[v][n]=1e9;
}
void w_solve(int v,int s,int w_fa,int w_pos)
{
// printf("solve(%d,%d,%d,%d)\n",v,s,w_fa,w_pos);
int w_temp=0;
if(w_pos==0)
{
if(!s)
{
w_id[v]=v;
w_min[v]=0;
for(int i=w_head[v];i;i=w_edge[i].w_next)
{
if(w_edge[i].w_to==w_fa)
continue;
w_solve(w_edge[i].w_to,1,v,1);
}
return;
}
if(f[v][s]==f[v][s-1])
{
w_solve(v,s-1,w_fa,0);
return;
}
w_temp=0;
for(int i=w_head[v];i;i=w_edge[i].w_next)
{
if(w_edge[i].w_to==w_fa)
continue;
w_temp=d[s]+f[w_edge[i].w_to][s-1];
for(int j=w_head[v];j;j=w_edge[j].w_next)
{
if(w_edge[j].w_to==w_fa||w_edge[j].w_to==w_edge[i].w_to)
continue;
if(g[w_edge[j].w_to][s+1]==1e9)
w_temp=1e9;
else
w_temp+=g[w_edge[j].w_to][s+1];
}
if(w_temp==f[v][s])
{
w_min[v]=s;
w_solve(w_edge[i].w_to,s-1,v,0);
for(int j=w_head[v];j;j=w_edge[j].w_next)
{
if(w_edge[j].w_to==w_fa||w_edge[j].w_to==w_edge[i].w_to)
continue;
w_solve(w_edge[j].w_to,s+1,v,1);
}
return;
}
}
}
else
{
if(g[v][s]==f[v][s])
{
w_solve(v,s,w_fa,0);
return;
}
w_min[v]=s;
for(int i=w_head[v];i;i=w_edge[i].w_next)
{
if(w_edge[i].w_to==w_fa)
continue;
w_solve(w_edge[i].w_to,s+1,v,1);
}
}
}
void w_bfs(int v)
{
while(!q.empty())
q.pop();
q.push({v,0});
while(!q.empty())
{
w_now=q.front();
q.pop();
if(w_dist[v][w_now.first])
continue;
w_dist[v][w_now.first]=w_now.second;
for(int i=w_head[w_now.first];i;i=w_edge[i].w_next)
q.push({w_edge[i].w_to,w_now.second+1});
}
}
int main()
{
scanf("%d %d",&n,&k);
for(int i=1;i<n;i++)
scanf("%d",&d[i]);
for(int i=1;i<n;i++)
{
scanf("%d %d",&x,&y);
w_add(x,y);
w_add(y,x);
}
for(int i=1;i<=n;i++)
w_bfs(i);
w_dfs(1,0);
w_cnt=0;
for(int i=1;i<=n;i++)
{
if(f[1][i]<f[1][w_cnt])
w_cnt=i;
}
printf("%d\n",f[1][w_cnt]);
w_solve(1,w_cnt,0,0);
for(int i=1;i<=n;i++)
{
if(w_id[i]==i)
continue;
for(int j=1;j<=n;j++)
{
if(w_id[j]!=j)
continue;
if(w_dist[i][j]==w_min[i])
{
w_id[i]=j;
break;
}
}
}
for(int i=1;i<=n;i++)
printf("%d ",w_id[i]);
return 0;
}
```
:::