换根

换根

用于解决树上假设每个点均为根的问题

思路:跑两遍dfs,第一遍假设一个节点为根,第二遍根据上一遍跑的尝试计算父节点的贡献并得出所有节点为根的情况

第二遍dfs一般思路为:如果为根节点,那么记录,否则将父节点纳入考虑中

在遍历所有子节点前记录现在的数据,要遍历那个子节点就取消该子节点的数据对该数据的影响再继续dfs,然后还原到原来的数据

P3478 [POI2008] STA-Station

题意:以一个节点为根使得所有节点深度之和最大

思路:维护以该节点为子节点的节点数量与总深度

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

const int N = 1e6 + 10;

vector<int> ed[N];

ll cnt[N],sumdep[N];

void dfs1(int son,int dad){
	cnt[son] = 1;
	for(auto v:ed[son]){
		if(v == dad) continue;
		dfs1(v,son);
		cnt[son] += cnt[v];
		sumdep[son] += cnt[v] + sumdep[v];
	}
}

pair<ll,ll> ans;

void dfs2(int son,int dad){
	if(son == 1){
		ans = {sumdep[1],1};
	}else{
		sumdep[son] += cnt[dad] + sumdep[dad];
		cnt[son] += cnt[dad];
		if(sumdep[son] > ans.first)
			ans = {sumdep[son],son};
	}
	ll rescnt = cnt[son],ressum = sumdep[son];
	for(auto v:ed[son]){
		if(v == dad) continue;
		cnt[son] -= cnt[v];
		sumdep[son] -= cnt[v] + sumdep[v];
		dfs2(v,son);
		cnt[son] = rescnt;
		sumdep[son] = ressum;
	}
}

int main(){
	IOS
	int n;cin >> n;
	for(int i = 1;i < n;i++){
		int u,v;cin >> u >> v;
		ed[u].push_back(v);
		ed[v].push_back(u);
	}
	dfs1(1,1);
	dfs2(1,1);
	cout << ans.second << endl;
}

P2986 [USACO10MAR] Great Cow Gathering G

题意:n个牛棚,n-1条路,每个牛棚有Ci只牛,不方便的程度为i1nCidisidisi为根到该点的距离,问最小的不方便值为多少

思路:维护牛的数量,总不方便值

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

const int N = 1e5 + 10;
ll cnt[N],sum[N],c[N];

vector<pair<int,int>> ed[N];

void dfs1(int son,int dad){
	cnt[son] += c[son];
	for(auto pa:ed[son]){
		int v = pa.first,dis = pa.second;
		if(v == dad) continue;
		dfs1(v,son);
		cnt[son] += cnt[v];
		sum[son] += sum[v] + cnt[v] * dis;
	}
}

ll ans;

void dfs2(int son,int dad,ll dis){
	if(son == 1){
		ans = sum[son];
	}else{
		cnt[son] += cnt[dad];
		sum[son] += cnt[dad] * dis + sum[dad];
		ans = min(sum[son],ans);
	}
	ll rescnt = cnt[son],ressum = sum[son];
	for(auto pa:ed[son]){
		int v = pa.first,_dis = pa.second;
		if(v == dad) continue;
		cnt[son] -= cnt[v];
		sum[son] -= cnt[v] * _dis + sum[v];
		dfs2(v,son,_dis);
		cnt[son] = rescnt;
		sum[son] = ressum;
	}
}

int main(){
	IOS
	int n;cin >> n;
	for(int i = 1;i <= n;i++)
		cin >> c[i];
	for(int i = 1;i < n;i++){
		int a,b,l;cin >> a >> b >> l;
		ed[a].push_back({b,l});
		ed[b].push_back({a,l});
	}
	dfs1(1,1);
	dfs2(1,1,0);
	cout << ans << endl;
}

E. Tree Painting

题意:给定一棵n个点的树 初始全是白点

要求你做n步操作,每一次选定一个与一个黑点相隔一条边的白点,将它染成黑点,然后获得该白点被染色前所在的白色联通块大小的权值。

第一次操作可以任意选点。

求可获得的最大权值

思路:维护数量与答案

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

const int N = 2e5 + 10;
ll cnt[N],sum[N];
vector<int> ed[N];

ll ans;
void dfs1(int son,int dad){
	cnt[son] = 1;
	for(auto v:ed[son]){
		if(v == dad) continue;
		dfs1(v,son);
		cnt[son] += cnt[v];
		sum[son] += cnt[v] + sum[v];
	}
}

void dfs2(int son,int dad){
	if(son == 1){
		ans = sum[1];
	}else{
		sum[son] += cnt[dad] + sum[dad];
		cnt[son] += cnt[dad];
		ans = max(sum[son],ans);
	}
	ll ressum = sum[son],rescnt = cnt[son];
	for(auto v:ed[son]){
		if(v == dad) continue;
		sum[son] -= sum[v] + cnt[v];
		cnt[son] -= cnt[v];
		dfs2(v,son);
		sum[son] = ressum;
		cnt[son] = rescnt;
	}
}

int main(){
	IOS
	int n;cin >> n;
	for(int i = 1;i < n;i++){
		int u,v;cin >> u >> v;
		ed[u].push_back(v);
		ed[v].push_back(u);
	}
	dfs1(1,1);
	dfs2(1,1);
	cout << ans + n << endl;
}

C. Centroids

等我水

posted @   xxcdsg  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示