arc098F Donation

题意:给你一张无向图,一开始携带有W元钱。走到x点,必须满足W>=Ax。可以选择在当前所在点捐献Bi的贡献(如果W<Bi,就不能做),并将此点标记。问标记完所有点,需要至少携带多少钱(最小W)?

n<=1e5。

标程:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 int read()
 5 {
 6    int x=0,f=1;char ch=getchar();
 7    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
 8    while (ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
 9    return x*f;
10 }
11 const int N=100005;
12 int cnt,head[N],a[N],b[N],id[N],u,v,fa[N],n,m,x,vis[N];
13 ll f[N],bsum[N];
14 struct node{int to,next;}num[N*2];
15 void add(int x,int y)
16 {num[++cnt].to=y;num[cnt].next=head[x];head[x]=cnt;}
17 bool cmp(int A,int B) {return a[A]<a[B];}
18 int getfather(int x) {return fa[x]?fa[x]=getfather(fa[x]):x;}
19 int main()
20 {
21     n=read();m=read();
22     for (int i=1;i<=n;i++) a[i]=read(),b[i]=read(),a[i]=max(a[i]-b[i],0),id[i]=i;
23     for (int i=1;i<=m;i++) u=read(),v=read(),add(u,v),add(v,u);
24     sort(id+1,id+n+1,cmp);
25     for (int i=1;i<=n;i++)
26     {
27         x=id[i];vis[x]=1;f[x]=a[x];bsum[x]=b[x];
28         for (int j=head[x];j;j=num[j].next)
29           if (vis[num[j].to])
30           {
31                 int y=getfather(num[j].to);//类似并查集找父亲,维护树形
32                 if (x==y) continue;
33                 fa[y]=x;bsum[x]+=bsum[y];
34                 f[x]=min(f[x],max(f[y],a[x]-bsum[y]));
35           }
36     }
37     printf("%lld
",bsum[id[n]]+f[id[n]]);
38    return 0;
39 }

题解:转换+性质神题

将限制合并成W-Bi>=max(Ai-Bi,0)。使得Ai=max(Ai-Bi,0)。

有结论:若Ax是当前的最大点,删去Ax后图被分成若干个连通块G1,G2,...,Gk。访问顺序G1,G2,...,Gk-1,x,Gk一定不劣。

[证明:如果先访问Gk中的某个点y,再访问x,由于Ax是当前最大,一定不会比先访问x后访问y的W答案来得小。]

因而,最后一次经过某个点时,我们把它捐掉。可以发现构成一棵树。有父亲的点捐完子树再往上走,没父亲的点捐完其他子树在最后一个子树中结束旅程。

在树上计算所需最小W,设bsum[i]为i子树中的B和。f[i]表示捐完当前子树所需的W比bsum[i]大多少。只用计算f[i]和最后经过的一个子树的不等式:1.W'>=Ai,即f[i]>=Ai-bsum[son]。W'表示捐完i点其他子树剩下的W,表示捐完其他子树后还能回到i点。2.W'>=bsum[son]+f[son],即f[i]>=f[son]。表示可以进入son子树遍历。取所有子树中max(f[son],Ai-bsum[son])中最小的一个作为最后经过更新f[i]。

小细节:由于是在图上建树,用并查集维护父亲,每次找到一棵子树的根连接,避免信息遗漏。

原文地址:https://www.cnblogs.com/Scx117/p/9118447.html