coci2011 debt 还债
coci2011 debt 还债
Description
有N个人,每个人恰好欠另一个人Bi元钱,现在大家都没有钱,政府想要给其中一些人欠,使得大家都不欠别人钱。
如A欠B 50,B欠C 20,则当政府给A50元时,A会立刻用50还清和B的债务,B也会立刻用20还清和C的债务。
欠款必须一次还清。如A欠B 50, B欠A50,你不能给A 49元让A还49元给B。
问政府至少花多少钱?
Input Format
第一行 N (2≤N ≤ 200,000) 表示居民数
接下来N行, 每行有两个数 Ai和 Bi, 表示第i位居民欠编号为Ai的居民Bi元钱 1 ≤ Ai≤ N, Ai ≠ i ) , 1 ≤Bi ≤10,000
Output Format
政府至少花多少钱
Sample Input
4
2 100
1 100
4 70
3 70
Sample Output
170
Solution
若i欠Ai Bi元钱,则让i向Ai连出一条边权为Bi的边,构成几个联通块。
每个联通块,有且仅有一个环。(由题目所给的“每个人恰好欠另一个人Bi元钱”可得)。
对于不在环上的点a,用拓扑排序处理 ,若a不足以支付债务则由政府支付。
对于环上的点b,设环外的居民欠他\(S_1\)元,环上的居民欠他\(S_2\)元,他欠别人\(P\)元。
则政府需要为环上每个居民支付\(max(P-S_1 +S_2,0)\)元钱,使得每个居民收到欠款之后有能力还债,并支付
所有环上居民中最小的\(max(P-S_1,0)\)使得有一个人能在未接受环上居民还钱的情况下能够还债。
Code
#include <cstdio>
#include <algorithm>
#define N 200007
inline int read(){
int num=0,k=1;char c=getchar();
while (c<'0'||c>'9'){if (c=='-')k=-1;c=getchar();}
while (c>='0'&&c<='9') num=(num<<1)+(num<<3)+c-48,c=getchar();
return num*k;
}
bool pd[N];
int n,ans,s,m[N],d[N],To[2][N],stac[N];
inline void dfs(int k,int pre){
if (pre){
ans+=std::max(0,To[1][k]-pre-m[k]);
s=std::min(s,std::max(0,To[1][k]-m[k]));
}
pd[k]=true;
if (!pd[To[0][k]]) dfs(To[0][k],To[1][k]);
else {
pre=To[1][k],k=To[0][k];
ans+=std::max(0,To[1][k]-pre-m[k]);
s=std::min(s,std::max(0,To[1][k]-m[k]));
}
}
int main(){
n=read();
for (int i=1;i<=n;++i)
To[0][i]=read(),To[1][i]=read(),++d[To[0][i]];
for (int i=1;i<=n;++i)
if (!d[i]) stac[++stac[0]]=i;
while (stac[0]){
int Top=stac[stac[0]--];
pd[Top]=true;
--d[To[0][Top]];
ans+=std::max(To[1][Top]-m[Top],0);
m[To[0][Top]]+=To[1][Top];
if (!d[To[0][Top]]) stac[++stac[0]]=To[0][Top];
}
for (int i=1;i<=n;++i)
if (!pd[i])
s=100007,dfs(i,0),ans+=s;
printf("%d\n",ans);
}