换根DP板子题
D-生活在树上_牛客小白月赛46 (nowcoder.com)
题意:
思路:
看数据范围是1e6且是统计问题,求的是对于每一个点的统计问题,那就逃不出是换根DP了
首先dfs1一次把树形DP求出来,然后再考虑换根
设dp[u][j]为以u为根的子树中,离根u的距离是j的结点个数
这个很容易转移,但是注意要对边权分类讨论
然后考虑换根,换根的时候注意是从上到下更新dp数组
是根据u结点的dp值和v的dp值更新儿子结点的dp2值,即dp2[v]=...dp[u]+....的形式
Code:
#include <bits/stdc++.h>
#define int long long
using namespace std;
using i64 = long long;
const int mxn=1e6+10;
const int mxe=1e6+10;
const int mod=1e9+7;
struct ty{
int to,next,w;
}edge[mxe<<2];
int N,Fa,d,tot=0;
int head[mxn],dp[mxn][3];
void add(int u,int v,int w){
edge[tot].w=w;
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
void G_init(){
tot=0;
for(int i=0;i<=N;i++){
head[i]=-1;
}
}
void dfs1(int u,int fa){
dp[u][0]=1;
dp[u][1]=dp[u][2]=0;
for(int i=head[u];~i;i=edge[i].next){
if(edge[i].to==fa) continue;
dfs1(edge[i].to,u);
if(edge[i].w==1){
dp[u][1]+=dp[edge[i].to][0];
dp[u][2]+=dp[edge[i].to][1];
}else if(edge[i].w==2){
dp[u][1]+=0;
dp[u][2]+=dp[edge[i].to][0];
}
}
}
void dfs2(int u,int fa){
for(int i=head[u];~i;i=edge[i].next){
if(edge[i].to==fa) continue;
if(edge[i].w==1){
dp[edge[i].to][1]+=dp[u][0];
dp[edge[i].to][2]+=dp[u][1]-1;
}else if(edge[i].w==2){
dp[edge[i].to][2]+=dp[u][0];
}
dfs2(edge[i].to,u);
}
}
void solve(){
cin>>N;
G_init();
for(int i=2;i<=N;i++){
cin>>Fa>>d;
add(i,Fa,d);
add(Fa,i,d);
}
dfs1(1,-1);
dfs2(1,-1);
for(int i=1;i<=N;i++) cout<<dp[i][0]+dp[i][1]+dp[i][2]<<'\n';
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int __=1;//cin>>__;
while(__--)solve();return 0;
}
但是注意dfs2不能写成如下形式:
void dfs2(int u,int fa){
dp2[u][0]=1;
for(int i=head[u];~i;i=edge[i].next){
if(edge[i].to==fa) continue;
if(edge[i].w==1){
dp2[edge[i].to][1]=dp[edge[i].to][1]+dp[u][0];
dp2[edge[i].to][2]=dp[edge[i].to][2]+dp[u][1]-1;
}else if(edge[i].w==2){
dp2[edge[i].to][1]=dp[edge[i].to][1];
dp2[edge[i].to][2]=dp[edge[i].to][2]+dp[u][0];
}
dfs2(edge[i].to,u);
}
}
因为父节点被不断更新,这里加上的应该是被更新过的父节点的dp值!
所以在写换根DP的时候,不能另开一个数组,而是累加