P8900
USACO 2022 Dec 银组 T1 Barn Tree
题意
给一棵无根树,每个点有点权,每次可以选一对有一条边连接的点,将其中一个的点权增加 \(\Delta\),另一个减少 \(\Delta\),操作过程中点权不能成负数。最终目标让所有点权相等,保证有解。输出最小操作次数以及具体操作。\(n\leq 2\times 10^5,边权\leq 10^9\)。
题解
无根树考虑以 \(1\) 为根。一对点的情况可以分为上传和下传。上传就是儿子上是多的,要把多的部分给父亲。下传则反过来。递归处理,最后让整个都一样。因为不能有负数,如果先下传就会出问题。所有先上传,再下传。因为小子树的情况一定是确定的,多的就往上拿就行,那么这个子树就不用动了。复杂度显然是 \(O(n)\)。aclink。
坑
- 记变化的数组一定要开
long long,这是因为一次变化可能包含一整棵子树的权,显然爆int。
代码
#include<bits/stdc++.h>
#define i64 long long
#define L(a,b,c,d) for(int a=b;a<=c;a+=d)
#define R(a,b,c,d) for(int a=b;a>=c;a-=d)
using namespace std;
const int N=2e5+5;
void solve();
int n,sum;
i64 a[N],f[N],p;
vector<int> e[N];
bool b[N];
signed main(){
int Test=1;
// scanf("%d",&Test);
while(Test--) solve();
return 0;
}
void dfs1(int u){
b[u]=1;
for(auto v:e[u]){
if(!b[v]){
dfs1(v);
f[v]=a[v]-p;
a[u]+=f[v];
if(f[v]) sum++;
}
}
}
void dfs2(int u){
b[u]=1;
for(auto v:e[u]){
if(!b[v]){
dfs2(v);
if(f[v]>0) printf("%d %d %lld\n",v,u,f[v]);
}
}
}
void dfs3(int u){
b[u]=1;
for(auto v:e[u]){
if(!b[v]){
if(f[v]<0) printf("%d %d %lld\n",u,v,-f[v]);
dfs3(v);
}
}
}
void solve(){
scanf("%d",&n);
i64 s=0;
L(i,1,n,1){
scanf("%lld",a+i);
s+=a[i];
}
p=s/n;
L(i,1,n-1,1){
int x,y;
scanf("%d%d",&x,&y);
e[x].push_back(y);
e[y].push_back(x);
}
dfs1(1);
memset(b,0,sizeof b);
printf("%d\n",sum);
dfs2(1);
memset(b,0,sizeof b);
dfs3(1);
}
本文来自博客园,作者:jess1ca1o0g3,转载请注明原文链接:https://www.cnblogs.com/jess1ca1o0g3/p/19051720

浙公网安备 33010602011771号