换根
用于解决树上假设每个点均为根的问题
思路:跑两遍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条路,每个牛棚有\(C_i\)只牛,不方便的程度为\(\sum _{i-1}^nC_idis_i\),\(dis_i\)为根到该点的距离,问最小的不方便值为多少
思路:维护牛的数量,总不方便值
#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;
}