【UVALive】7338 Toll Management IV

Discription

就给个大意了:给定一张图,问每条边最多能增加和减少多少,使得原本的最小生成树还是最小生成树。

Solution

思想:破环法

对于非树边,它可以无限增加(也不会成为生成树的一部分)
它最多能减少到它和这棵树构成的那个环中,除它自己以外最大边的长度

对于每条树边,它可以无限减少
它增加的范围是到其刚好可被替代,也就是所有包含这条边的环的最大边的最小值。
因为环由树边和非树边构成,所有非树边权值不小于树边
那么就可以说明,假如以两条(或以上)非树边和若干条树边构成了一个环,那么它对答案的限制不会强于以这些非树边分别和若干条树边构成的环。(因为限制由最长边贡献)

那么就可以通过维护链的最大值,以及对链取最小值两个操作,完成题目
(我写的倍增,懒癌晚期)

顺便推个大神,如果一直调不过的,他那里还有免费的造数据程序可以偷
http://blog.csdn.net/di4CoveRy/article/details/53509506

#include<stdio.h>
#include<algorithm>
#include<cstring>
#define Min(a,b) a=min(a,b)
#define Max(a,b) a=max(a,b)
#define N 10003
#define M 200003

typedef long long ll;

int dep[N],d[N][15],g[N][14],tag[N][14],tot,T,n,m,s[N],A[M],B[M],C[M];
ll S;

using namespace std;

struct edge{int v,c,n;}e[M];

inline void push(const int &a,const int &b,const int &c){e[++tot]=(edge){b,c,s[a]};s[a]=tot;}

void dfs(const int &k,const int &f,const int &c)
{
    dep[k]=dep[f]+1;
    d[k][0]=f;g[k][0]=c;
    for (int p=1;(1<<p)<dep[k];p++)
    {
        d[k][p]=d[d[k][p-1]][p-1];
        g[k][p]=max(g[k][p-1],g[d[k][p-1]][p-1]);
    }
    for (int i=s[k];i;i=e[i].n) if (e[i].v!=f) dfs(e[i].v,k,e[i].c);
}

inline void solve(int u,int v,const int &c,const int &cas)
{
    int ans=0;
    if (dep[u]<dep[v]) swap(u,v);
    for (int i=13;0<=i;i--) if ((1<<i)<=dep[u]-dep[v])
    {
        Min(tag[u][i],c);
        Max(ans,g[u][i]);
        u=d[u][i];
    }
    if (u!=v)
    {
        for (int i=13;0<=i;i--) if (d[u][i]!=d[v][i])
        {
            Min(tag[u][i],c);Max(ans,g[u][i]);
            Min(tag[v][i],c);Max(ans,g[v][i]);
            u=d[u][i];v=d[v][i];
        }
        Min(tag[u][0],c);Max(ans,g[u][0]);
        Min(tag[v][0],c);Max(ans,g[v][0]);
    }
    S+=-cas+(ll)cas*cas*(c-ans);
}

int main()
{
    scanf("%d",&T);
    for (int t=1;t<=T;t++)
    {
        S=0;
        memset(s,0,sizeof(s));
        memset(d,0,sizeof(d));
        scanf("%d%d",&n,&m);tot=0;
        for (int i=1;i<n;i++) scanf("%d%d%d",A+i,B+i,C+i),
         push(A[i],B[i],C[i]),push(B[i],A[i],C[i]);
        for (int i=n;i<=m;i++) scanf("%d%d%d",A+i,B+i,C+i);
        dfs(1,0,0);
        memset(tag,0x3f,sizeof(tag));int inf=tag[0][0];
        for (int i=n;i<=m;i++) solve(A[i],B[i],C[i],i);
        for (int p=13;p;p--) for (int i=1;i<=n;i++) if ((1<<p)<dep[i]) 
         Min(tag[i][p-1],tag[i][p]),Min(tag[d[i][p-1]][p-1],tag[i][p]);
        for (int i=1;i<n;i++)
        {
            if (d[B[i]][0]==A[i]) swap(A[i],B[i]);
            S+=i*(tag[A[i]][0]==inf?-1:tag[A[i]][0]-C[i])-i*i;
        }
        printf("Case %d: %lld\n",t,S);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值