换根dp
典题:
题目链接:https://www.luogu.com.cn/problem/P2986
题意:
给定一棵树(有边权且有点权),选取一个节点作为根节点,使得 其他节点到根节点的距离 x 点权 之和最小
思路:
换根dp,从根节点为1开始进行状态转移
观察可得,根节点每往相邻子节点移动一格,相邻子节点方向的 答案贡献减少,而父节点方向的 答案贡献增加
具体来说,转移后的节点答案相较于 父节点答案 减少了 相邻子节点方向的权值和x转移边边长, 增加了 父节点方向的权值和x转移边边长
一次dfs容易得到根节点为1时的答案,同时得到了每个子树的权值和
观察题目没有考察容斥
int c[maxn];
vector<pii>e[maxn];
int res[maxn];
int siz[maxn];
int ans;
int n;
int sum;
void dfs(int u,int fa,int k){
siz[u]+=c[u];
ans+=k*c[u];
for(pii x:e[u]){
int v=x.fi,len=x.se;
if(v==fa)continue;
dfs(v,u,k+len);
siz[u]+=siz[v];
}
}
void dfs2(int u,int fa,int k){
if(u!=1){
res[u]=res[fa]-siz[u]*k+(sum-siz[u])*k;
}
for(pii x:e[u]){
int v=x.fi,len=x.se;
if(v==fa)continue;
// res[v]=res[u]-siz[v]*len+(sum-siz[v])*len;
dfs2(v,u,len);
}
}
void solve(){
cin>>n;
rep(i,1,n){
cin>>c[i];
sum+=c[i];
}
rep(i,1,n-1){
int u,v,l;cin>>u>>v>>l;
e[u].pb({v,l});
e[v].pb({u,l});
}
dfs(1,0,0);
res[1]=ans;
dfs2(1,0,0);
int temp=llmax;
for(int i=1;i<=n;i++){
// debug(res[i]);
temp=min(temp,res[i]);
}
cout<<temp<<endl;
}
题目链接:https://www.luogu.com.cn/problem/P3047
题意:
求树上每一个节点与其距离不超过k的所有节点的权值和
思路:
记f[i][j]:与i节点距离小于等于j的节点权值和(子树方向)
d[i][j]:与i节点距离小于等于j的节点权值和(子树方向+父节点方向)
那么答案为d[i][k]
一次dfs易求得f[i][j],转移方程:f[u][j]+=f[v][j-1]
注意f[u][j]需要累加u节点自身的权值
第二次dfs,转移方程:d[u][j] = d[fa][j-1]-f[u][j-2] + f[u][j]
当j为1时d[u][j]=f[u][j]+f[fa][0]
当u节点作为根节点时,与它距离小于等于j的权值和 = u子树方向的权值和 + u父节点方向的权值和
其中u子树方向的权值和容易得到为f[u][j]
容斥:u父节点的方向权值= u父节点作为根节点,与它距离小于等于j-1的权值和 - u子树方向与u距离小于等于j-2的权值和
由于根节点的转移才导致-1和-2
vector<int>e[maxn];
int c[maxn];
int f[maxn][25];
int n,k;
int d[maxn][25];
void dfs(int u,int fa){
for(int i=0;i<=k;i++){
f[u][i]+=c[u];
}
for(int v:e[u]){
if(v==fa)continue;
dfs(v,u);
for(int j=1;j<=k;j++){
f[u][j]+=f[v][j-1];
}
}
}
void dfs2(int u,int fa){
if(u!=1){
d[u][1]+=f[fa][0];
for(int j=2;j<=k;j++){
d[u][j]+=d[fa][j-1]-f[u][j-2];
}
}
for(int v:e[u]){
if(v==fa)continue;
dfs2(v,u);
}
}
void solve(){
cin>>n>>k;
rep(i,1,n-1){
int u,v;cin>>u>>v;
e[u].pb(v);e[v].pb(u);
}
rep(i,1,n){
cin>>c[i];
}
dfs(1,0);
for(int i=1;i<=n;i++){
for(int j=0;j<=k;j++){
d[i][j]=f[i][j];
}
}
dfs2(1,0);
for(int i=1;i<=n;i++){
cout<<d[i][k]<<endl;
}
}

浙公网安备 33010602011771号