电报

题目大意

n个点,每个点出度均为1的有向图。
你可以将j->k改成j->l,代价为c[j]。
求最小代价,使得有向图变成一个环。

贪心

我们把问题描述成删去若干条边,删除每条边都有代价,最小代价使得每个联通块都是一条链(这样才能连成环)。
假如一个点有k个入度,至少k-1个要被删掉。
对于树的情况贪心保留最大代价的入边。
环上至少一条边要删去,再讨论一下即可。
注意特判初始所有点连成一个环的情况。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=100000+10,inf=2000000000;
int a[maxn],b[maxn],c[maxn],g[maxn],p[maxn],belong[maxn];
int h[maxn],go[maxn],nxt[maxn];
int h2[maxn],g2[maxn],n2[maxn];
ll f[maxn];
bool pd[maxn],bz[maxn];
int i,j,k,l,t,n,m,tot,top,cnt,wdc,mi;
ll ans,sum,num;
bool czy;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void add(int x,int y){
    go[++tot]=y;
    nxt[tot]=h[x];
    h[x]=tot;
}
void add2(int x,int y){
    g2[++cnt]=y;
    n2[cnt]=h2[x];
    h2[x]=cnt;
}
void dfs(int x){
    if (pd[a[x]]){
        belong[x]=++tot;
        bz[x]=1;
        wdc=a[x];
        return;
    }
    if (belong[a[x]]){
        belong[x]=belong[a[x]];
        return;
    }
    pd[x]=1;
    dfs(a[x]);
    belong[x]=belong[a[x]];
    if (wdc) bz[x]=1;
    if (x==wdc) wdc=0;
    pd[x]=0;
}
void dg(int x){
    int t=h[x],mx=0;
    ll l=0;
    while (t){
        dg(go[t]);
        mx=max(mx,c[go[t]]);
        l+=(ll)c[go[t]];
        t=nxt[t];
    }
    ans+=(ll)l-mx;
}
void work(int x){
    top=0;
    t=h2[x];
    while (t){
        p[++top]=g2[t];
        t=n2[t];
    }
    fo(i,1,top)
        if (!bz[p[i]]&&bz[a[p[i]]]){
            dg(p[i]);
            f[a[p[i]]]+=(ll)c[p[i]];
            g[a[p[i]]]=max(g[a[p[i]]],c[p[i]]);
        }
    fo(i,1,top)
        if (bz[p[i]]) b[a[p[i]]]=c[p[i]];
    mi=inf;
    fo(i,1,top)
        if (bz[p[i]]) mi=min(mi,c[p[i]]);
    sum=0;
    fo(i,1,top)
        if (bz[p[i]]) sum+=f[p[i]];
    num=sum+mi;
    sum=0;
    fo(i,1,top)
        if (bz[p[i]]) sum+=min(f[p[i]],f[p[i]]-g[p[i]]+(ll)b[p[i]]);
    fo(i,1,top)
        if (bz[p[i]]){
            sum-=min(f[p[i]],f[p[i]]-g[p[i]]+(ll)b[p[i]]);
            num=min(num,sum+f[p[i]]-g[p[i]]+(ll)b[p[i]]);
            sum+=min(f[p[i]],f[p[i]]-g[p[i]]+(ll)b[p[i]]);
        }
    ans+=num;
}
int main(){
    freopen("telegraph.in","r",stdin);freopen("telegraph.out","w",stdout);
    n=read();
    fo(i,1,n){
        a[i]=read();c[i]=read();
        add(a[i],i);
    }
    tot=0;
    fo(i,1,n)
        if (!belong[i]){
            wdc=0;
            dfs(i);
        }
    if (tot==1){
        czy=1;
        fo(i,1,n)
            if (!bz[i]){
                czy=0;
                break;
            }
        if (czy){
            printf("0\n");
            return 0;
        }
    }
    fo(i,1,n) add2(belong[i],i);
    fo(wdc,1,tot) work(wdc);
    printf("%lld\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值