其他分享
首页 > 其他分享> > 换根dp学习笔记

换根dp学习笔记

作者:互联网

换根DP

换 根 d p 一 般 的 三 步 骤 换根dp一般的三步骤 换根dp一般的三步骤:

  1. 任意选择一个节点作为根
  2. 统计子树(自底向上)对每个根节点的贡献
  3. (自顶向下)统计父节点对子节点的贡献并计算答案

Problem A:P3478 [POI2008]STA-Station

// Problem: P3478 [POI2008]STA-Station
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3478
// Memory Limit: 125 MB
// Time Limit: 2000 ms

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define pb push_back 
#define in insert
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo_(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;

template<typename T>
ostream& operator<<(ostream& os,const vector<T>&v){for(int i=0,j=0;i<v.size();i++,j++)if(j>=5){j=0;puts("");}else os<<v[i]<<" ";return os;}
template<typename T>
ostream& operator<<(ostream& os,const set<T>&v){for(auto c:v)os<<c<<" ";return os;}

typedef pair<int,int>PII;
typedef pair<long,long>PLL;

typedef long long ll;
typedef unsigned long long ull; 
const int N=1e6+10,M=1e9+7;
ll n,m,_;

int h[N],ne[N*2],e[N*2],idx;

int dis[N],cnt[N];

void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void dfs(int u,int fa)
{
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(j==fa)continue;
		
		cnt[u]+=cnt[j];	
		dfs(j,u);

		dis[u]=max(dis[u],dis[j]+cnt[j]+1);
	}
	
	if(!cnt[u])cnt[u]=1;//叶子节点
}


void solve()
{
	cin>>n;
	mem(h,-1);
	for(int i=1;i<=n-1;i++)
	{
		int u,v;scanf("%d%d",&u,&v);
		add(u,v),add(v,u);
	}
	
	dfs(1,-1);
	
	for(int i=1;i<=n;i++)
	{
		cout<<i<<" "<<cnt[i]<<" "<<dis[i]<<endl;
	}
}

int main()
{
	solve();
	return 0;
}
	

今天要疯狂一下!

刚才好像体会到了大师我悟了的感觉

Q_1

给 定 一 棵 树 , 以 1 号 节 点 为 根 节 点 , 统 计 树 种 每 个 节 点 的 深 入 记 入 d e p [ N ] 数 组 , 统 计 以 每 个 节 点 u 为 根 的 子 树 的 节 点 数 目 计 入 c n t [ N ] 数 组 给定一棵树,以1号节点为根节点,统计树种每个节点的深入记入dep[N]数组,\\ 统计以每个节点u为根的子树的节点数目计入cnt[N]数组 给定一棵树,以1号节点为根节点,统计树种每个节点的深入记入dep[N]数组,统计以每个节点u为根的子树的节点数目计入cnt[N]数组


体 会 深 度 是 从 根 节 点 , 自 顶 向 下 递 推 得 到 的 , 子 节 点 树 木 是 从 树 的 底 部 , 自 底 向 上 递 推 得 到 的 , 那 么 树 形 d p 种 的 换 根 也 是 一 样 , 两 种 递 推 方 式 对 应 的 代 码 如 下 体会深度是从根节点,自顶向下递推得到的,子节点树木是从树的底部,自底向上\\ 递推得到的,那么树形dp种的换根也是一样,两种递推方式对应的代码如下 体会深度是从根节点,自顶向下递推得到的,子节点树木是从树的底部,自底向上递推得到的,那么树形dp种的换根也是一样,两种递推方式对应的代码如下


void init()
{
    for(int i=1;i<=n;i++)cnt[i]=1;
}
void dfs(int u,int fa)
{
    for(int i=h[u];~i;i=ne[i])
    {
        int j=e[i];
        if(j==fa)continue;
        dep[j]=dep[u]+1;//由根节点更新子节点
        
        dfs(j,u);
        
        cnt[u]+=cnt[j];//得到了子节点的相关信息,由子节点更新根节点
    }
}

Problem B:P2986 [USACO10MAR]Great Cow Gathering G


说 实 话 , 写 了 一 半 的 时 候 , 突 然 收 到 数 字 逻 辑 下 周 周 四 考 的 消 息 , 我 T M 还 没 复 习 呢 , 在 纸 上 写 了 写 , 自 己 的 草 稿 是 真 的 烂 , 而 且 不 会 分 析 换 根 之 后 根 对 子 节 点 的 影 响 只 写 了 第 一 个 d f s , 然 后 写 了 一 个 暴 力 说实话,写了一半的时候,突然收到数字逻辑下周周四考的消息,\\ 我TM还没复习呢,在纸上写了写,自己的草稿是真的烂,\\ 而且不会分析换根之后根对子节点的影响 只写了第一个dfs,然后写了一个暴力 说实话,写了一半的时候,突然收到数字逻辑下周周四考的消息,我TM还没复习呢,在纸上写了写,自己的草稿是真的烂,而且不会分析换根之后根对子节点的影响只写了第一个dfs,然后写了一个暴力

又因为%lld写成了%%lld一直debug
然 后 是 c n t [ u ] + = c n t [ j ] , 写 成 了 c n t [ u ] + = W [ j ] 才 写 好 暴 力 , 太 菜 了 吧 ! 然后是cnt[u]+=cnt[j],写成了cnt[u]+=W[j]才写好暴力,太菜了吧! 然后是cnt[u]+=cnt[j],写成了cnt[u]+=W[j]才写好暴力,太菜了吧!

// Problem: P2986 [USACO10MAR]Great Cow Gathering G
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2986
// Memory Limit: 125 MB
// Time Limit: 1000 ms

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define pb push_back 
#define in insert
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo_(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;

template<typename T>
ostream& operator<<(ostream& os,const vector<T>&v){for(int i=0,j=0;i<v.size();i++,j++)if(j>=5){j=0;puts("");}else os<<v[i]<<" ";return os;}
template<typename T>
ostream& operator<<(ostream& os,const set<T>&v){for(auto c:v)os<<c<<" ";return os;}

typedef pair<int,int>PII;
typedef pair<long,long>PLL;

typedef long long ll;
typedef unsigned long long ull; 
const int N=1e5+10,M=1e9+7;
ll n,m,_;

int h[N],ne[N*2],e[N*2],idx;
ll w[N*2],W[N];//边权,点权

ll g[N],cnt[N];

void dfs(int u,int fa)
{
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(j==fa)continue;
		
		dfs(j,u);
		cnt[u]+=cnt[j];
		g[u]+=g[j]+w[i]*cnt[j];
		
	}
}

void add(int a,int b,ll c)
{
	e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}

void init()
{
	for(int i=1;i<=n;i++)
		cnt[i]=W[i],g[i]=0;
}

void solve()
{
	cin>>n;
	mem(h,-1);
	for(int i=1;i<=n;i++)scanf("%lld",&W[i]);
	 
	for(int i=1;i<n;i++)
	{
		int a,b;
		ll c;
		scanf("%d%d%lld",&a,&b,&c);
		add(a,b,c);
		add(b,a,c);
	}

	ll ans=0x3f3f3f3f3f3f3f3f;
	
	for(int i=1;i<=n;i++)
	{
		init();
		dfs(i,-1);
		ans=min(ans,g[i]);
	}
	cout<<ans<<endl;
	
}

int main()
{
	solve();
	return 0;
}

全程自己验算,各种debug,没看题解推导出来了,太开心了

// Problem: P2986 [USACO10MAR]Great Cow Gathering G
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2986
// Memory Limit: 125 MB
// Time Limit: 1000 ms

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define pb push_back 
#define in insert
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo_(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;

template<typename T>
ostream& operator<<(ostream& os,const vector<T>&v){for(int i=0,j=0;i<v.size();i++,j++)if(j>=5){j=0;puts("");}else os<<v[i]<<" ";return os;}
template<typename T>
ostream& operator<<(ostream& os,const set<T>&v){for(auto c:v)os<<c<<" ";return os;}

typedef pair<int,int>PII;
typedef pair<long,long>PLL;

typedef long long ll;
typedef unsigned long long ull; 
const int N=1e5+10,M=1e9+7;
ll n,m,_;

int h[N],ne[N*2],e[N*2],idx;
ll w[N*2],W[N];//边权,点权

ll g[N],cnt[N];//sum==cnt[1]; 需要g[1],dfs_up不需要g了
ll f[N];

void dfs(int u,int fa)
{
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(j==fa)continue;
		
		dfs(j,u);
		cnt[u]+=cnt[j];
		g[u]+=g[j]+w[i]*cnt[j];
		
	}
}

void dfs_up(int u,int fa)
{
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(j==fa)continue;
		
		f[j]=f[u]+w[i]*(cnt[1]-2*cnt[j]);
		
		dfs_up(j,u);
	}
}

void add(int a,int b,ll c)
{
	e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}

void solve()
{
	cin>>n;
	mem(h,-1);
	for(int i=1;i<=n;i++)scanf("%lld",&W[i]);
	 
	for(int i=1;i<n;i++)
	{
		int a,b;
		ll c;
		scanf("%d%d%lld",&a,&b,&c);
		add(a,b,c);
		add(b,a,c);
	}
	
	for(int i=1;i<=n;i++)
		cnt[i]=W[i];

	dfs(1,-1);
	f[1]=g[1];//NB!
	dfs_up(1,-1);
	ll ans=0x3f3f3f3f3f3f3f3f;
	
	for(int i=1;i<=n;i++)
	{
		ans=min(ans,f[i]);
	}
	cout<<ans<<endl;
	
}

int main()
{
	solve();
	return 0;
}

Problem C:Tree with Maximum Cost

超级开心,自己写出来了(虽然有点水)某次div3的F题

// Problem: F. Tree with Maximum Cost
// Contest: Codeforces - Codeforces Round #527 (Div. 3)
// URL: https://codeforces.com/problemset/problem/1092/F
// Memory Limit: 256 MB
// Time Limit: 2000 ms

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define pb push_back 
#define in insert
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo_(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;

template<typename T>
ostream& operator<<(ostream& os,const vector<T>&v){for(int i=0,j=0;i<v.size();i++,j++)if(j>=5){j=0;puts("");}else os<<v[i]<<" ";return os;}
template<typename T>
ostream& operator<<(ostream& os,const set<T>&v){for(auto c:v)os<<c<<" ";return os;}

typedef pair<int,int>PII;
typedef pair<long,long>PLL;

typedef long long ll;
typedef unsigned long long ull; 
const int N=2e5+10,M=1e9+7;
ll n,m,_;

int h[N],ne[N*2],e[N*2],idx;
ll w[N];


void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

ll Sum,sum[N];//原来是sum[N]忘记抛弃自己了
ll g[N],f[N];
void dfs(int u,int fa)
{
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(j==fa)continue;
		
		dfs(j,u);
		
		sum[u]+=sum[j]+w[j];
		
		g[u]+=g[j]+sum[j]+w[j];
	}
}

void dfs_up(int u,int fa)
{
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(j==fa)continue;
		
		
		// f[j]=g[j]+f[u]-(w[j]+g[j]+sum[j])+(Sum-sum[j]-w[j]);
		
		f[j]=f[u]-2*w[j]-2*sum[j]+Sum;//化简之后,发现一个规律,不会用到g[j]的,而且-2*w[j]
		dfs_up(j,u);
	}
}

void solve()
{
	cin>>n;
	mem(h,-1);
	for(int i=1;i<=n;i++)
		scanf("%lld",&w[i]);
	fo(i,1,n-1)
	{
		int u,v;scanf("%d%d",&u,&v);
		add(u,v);add(v,u);
	}
	
	dfs(1,-1);
	
	Sum=sum[1]+w[1];//Sum=
	
	// debug(Sum);
	f[1]=g[1];
	
	// for(int i=1;i<=n;i++)//没问题
	// {
		// cout<<i<<" "<<sum[i]<<" "<<g[i]<<endl;
	// }
	// cout<<g[1]<<endl;//没问题
	dfs_up(1,-1);
	
	ll ans=-1;
	for(int i=1;i<=n;i++)
	{
		ans=max(ans,f[i]);
		// cout<<i<<" "<<f[i]<<endl;		
	}

	cout<<ans<<endl;
}

int main()
{	
	solve();
	return 0;
}

Problem DP3047 [USACO12FEB]Nearby Cows G


全 程 手 推 , 自 己 d e b u g , 虽 然 知 道 了 树 形 d p 的 套 路 , 但 是 还 是 做 的 有 点 慢 , 不 过 最 后 所 有 的 数 据 和 自 己 想 到 的 都 完 全 一 样 的 时 候 , 真 的 超 级 开 心 全程手推,自己debug,虽然知道了树形dp的套路,但是还是做的有点慢,不过最后 \\ 所有的数据和自己想到的都完全一样的时候,真的超级开心 全程手推,自己debug,虽然知道了树形dp的套路,但是还是做的有点慢,不过最后所有的数据和自己想到的都完全一样的时候,真的超级开心

又悟道了一点
    写的时候我觉得dp的时候就向背包一样需要倒序遍历,经过实验发现不需要,
    其实因为是树形dp,那么dfs之后,通过j更新父节点u的时候一定是不同层的,
    所以正序和倒序都对。
  	for(int m=0;m<=k;m++)
        ans[j][m]=f[j][m]+ans[u][m-1]-f[j][m-2];
    for(int m=k;m;m--)
    {
        ans[j][m]=f[j][m]+ans[u][m-1]-f[j][m-2];
    }
// Problem: P3047 [USACO12FEB]Nearby Cows G
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3047
// Memory Limit: 125 MB
// Time Limit: 1000 ms

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define pb push_back 
#define in insert
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo_(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;

template<typename T>
ostream& operator<<(ostream& os,const vector<T>&v){for(int i=0,j=0;i<v.size();i++,j++)if(j>=5){j=0;puts("");}else os<<v[i]<<" ";return os;}
template<typename T>
ostream& operator<<(ostream& os,const set<T>&v){for(auto c:v)os<<c<<" ";return os;}

typedef pair<int,int>PII;
typedef pair<long,long>PLL;

typedef long long ll;
typedef unsigned long long ull; 
const int N=2e5+10,M=1e9+7;
ll n,m,_,k;

int h[N],e[N],ne[N],idx;
ll w[N];

void  add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

ll f[N][25];
ll ans[N][25];//res==max(res,ans[i][k]);

void dfs(int u,int fa)
{
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(j==fa)continue;
		
		dfs(j,u);
		
		
		for(int m=1;m<=k;m++)
			f[u][m]+=f[j][m-1];
// 		
		// for(int m=k;m>=1;m--)
		// {
			// f[u][m]+=f[j][m-1];
		// }
	}
}

void Dfs(int u,int fa)
{
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(j==fa)continue;
		
		for(int m=0;m<=k;m++)
			ans[j][m]=f[j][m]+ans[u][m-1]-f[j][m-2];
		
		// for(int m=k;m;m--)
		// {
			// ans[j][m]=f[j][m]+ans[u][m-1]-f[j][m-2];
		// }
// 		
		Dfs(j,u);
	}
}


void solve()
{
	cin>>n>>k;
	mem(h,-1);
	for(int i=1;i<n;i++)
	{
		int u,v;scanf("%d%d",&u,&v);
		add(u,v);add(v,u);
	}
	for(int i=1;i<=n;i++)scanf("%lld",&w[i]);
	for(int i=1;i<=n;i++)//初始化就很nice
	{
		f[i][0]=w[i];
		for(int j=1;j<=k;j++)
			f[i][j]+=f[i][0];	
	}
	
	dfs(1,-1);
	
	// for(int i=1;i<=n;i++)//正确
	// {
		// for(int j=0;j<=k;j++)
			// cout<<f[i][j]<<" ";
		// cout<<endl;
	// }
	

	
	
	for(int i=0;i<=k;i++)
	{
		ans[1][i]=f[1][i];
	}
	
	for(int i=1;i<=n;i++)
		ans[i][0]=f[i][0];
	
	
	Dfs(1,-1);
	
	
	for(int i=1;i<=n;i++)
	{
		cout<<ans[i][k]<<endl;
	}
}

int main()
{
	solve();
	return 0;
}

标签:cnt,idx,int,ll,笔记,dfs,include,换根,dp
来源: https://blog.csdn.net/hacker__man/article/details/117534619