Loading

P8900

USACO 2022 Dec 银组 T1 Barn Tree

link

题意

给一棵无根树,每个点有点权,每次可以选一对有一条边连接的点,将其中一个的点权增加 \(\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);
}
posted @ 2025-08-21 22:40  jess1ca1o0g3  阅读(5)  评论(0)    收藏  举报