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);
}

转载于:https://www.cnblogs.com/hyheng/p/7763771.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值