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);
}
posted @ 2017-10-31 21:18  Hyheng  阅读(341)  评论(0编辑  收藏  举报