AT4144 [ARC098D] Donation【2019集训队作业】
AT4144 [ARC098D] Donation【2019集训队作业】
C u = m a x { A u − B u , 0 } C_u=max\{A_u-B_u,0\} Cu=max{Au−Bu,0}
这 C u C_u Cu 的实际意义表示的是任意时刻在 u u u 点的钱数都会大于 c u c_u cu 。
直接把 C C C 从小到大排序,然后建重构树即可。
d p x dp_x dpx 表示 x x x 子树内的答案。
那么有转移:
d p x = min { s u m b x − s u m b y + max { d p y , C x } } dp_x=\min\{sumb_x-sumb_y+\max\{dp_y,C_x\}\} dpx=min{sumbx−sumby+max{dpy,Cx}}
s u m b x sumb_x sumbx 表示 x x x 子树内 B B B 的和。
选中的 y y y 就是我们最后遍历到的连通块,前面连通块遍历顺序不关心。
#include <bits/stdc++.h>
#define N 100005
using namespace std;
typedef long long ll;
int n,m;
struct node{
int a,b,id;
}cc[N];
bool cmp_A(node x,node y){
return x.a-x.b==y.a-y.b?x.id<y.id:x.a-x.b<y.a-y.b;
}
vector<int> son[N],ed[N];
int fa[N],A[N],B[N],co[N];
ll ans[N],s[N];
int findf(int x){ return fa[x]==x?x:fa[x]=findf(fa[x]); }
void dfs(int x){
int y; s[x]=B[x]; ans[x]=1e15;
if(!ed[x].size()) ans[x]=max(A[x],B[x]);
for(int i=0;i<ed[x].size();i++){
y=ed[x][i]; dfs(y); s[x]+=s[y];
}
for(int i=0;i<ed[x].size();i++){
y=ed[x][i]; ans[x]=min(ans[x],s[x]-s[y]+max((ll)A[x]-B[x],ans[y]));
}
// cout<<x<<' '<<ans[x]<<'\n';
}
int main(){
// freopen("test.in","r",stdin);
cin>>n>>m;
int u,v;
for(int i=1;i<=n;i++) cin>>A[i]>>B[i],cc[i].a=A[i],cc[i].b=B[i],cc[i].id=i,fa[i]=i;
for(int i=1;i<=m;i++){
cin>>u>>v;
son[u].push_back(v),son[v].push_back(u);
}
sort(cc+1,cc+1+n,cmp_A);
int x,y;
for(int i=1;i<=n;i++){
x=cc[i].id;
for(int j=0;j<son[x].size();j++){
y=son[x][j];
if(A[y]-B[y]>A[x]-B[x]||(A[y]-B[y]==A[x]-B[x]&&y>x)) continue;
if(findf(y)!=findf(x)){
ed[x].push_back(fa[y]);
fa[fa[y]]=fa[x];
}
}
}
dfs(findf(1));
cout<<ans[findf(1)];
}