AT4144 [ARC098D] Donation
首先这个形式看上去正着不太好做,我们考虑算最终至少会有多少钱。
为此,我们可以选定一个终点,显然最终剩余的钱数要大于等于\(A_x-B_x\)。容易发现,从终点开始反向遍历,要满足到一个点之后至少要有\(A_x-B_x\),在第一次到达这个点之后会加上\(B_x\)元。
以\(A_x-B_x\)为点权建立点权的克鲁斯卡尔重构树,每次将子树内的点遍历完之后会让钱数达到最大,然后再跳到父亲计算当前至少要最后剩多少钱,前缀和即可。
时间复杂度\(O(n\log n)\)
code:
#include<bits/stdc++.h>
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=2e5+5,M=pow(6,10)+5,K=2e3+5,mod=1e9+7,Mod=mod-1;const db eps=1e-5;const int INF=1e9+7;
int n,m,Rt,A[N],B[N],x,y,C[N],Fl[N],fa[N],Ct;ll Si[N],Ans=1e18;vector<int> S[N],G[N];
bool cmp(int x,int y){return A[x]-B[x]<A[y]-B[y];}
int GF(int x){return fa[x]^x?fa[x]=GF(fa[x]):x;}
void Make(int x){Si[x]=B[x];for(int i:S[x]) Make(i),Si[x]+=Si[i];}
void GA(int x,ll w){Ans=min(Ans,max(w,1ll*A[x]-B[x]));for(int i:S[x]) GA(i,max(w,A[x]-B[x]-Si[i]));}
int main(){
freopen("1.in","r",stdin);
int i,j;scanf("%d%d",&n,&m);for(i=1;i<=n;i++) scanf("%d%d",&A[i],&B[i]),A[i]=max(A[i],B[i]),fa[i]=C[i]=i;while(m--) scanf("%d%d",&x,&y),G[x].PB(y),G[y].PB(x);
sort(C+1,C+n+1,cmp);for(i=1;i<=n;i++){Fl[C[i]]=1;for(int j:G[C[i]]) Fl[j]&&GF(j)^GF(C[i])&&(S[GF(C[i])].PB(GF(j)),cerr<<GF(C[i])<<' '<<GF(j)<<'\n',fa[GF(j)]=GF(C[i]));}
Make(Rt=GF(1));GA(Rt,-1e18);for(i=1;i<=n;i++) Ans+=B[i];printf("%lld\n",Ans);
}