Uva 12587 Reduce the Maintenance Cost 边双连通+树形dp+贪心

题目链接:点击打开链接


题意:

给定n个点m条边的无向图

下面n个数表示每个点的权值。

下面m条边 (u,v) dis

对于每条边,这条边的权值为 原图上删除这条边后,有N对点被分离, 则点权为 N*dis

然后:

让边两端任意一个点的点权上 加上这条边的权值

使得:

最大的点权最小。

问:最小的答案。

思路:

1、首先我们计算每条边的边权。

除了桥,边权都是0,因为不会有点被分离。

所以边双连通缩点一下,得到桥,然后计算一下每条边的边权。

2、

二分一下答案。

对于一棵树,先走到叶子节点,然后把每个点的父边的边权加到这个叶子节点上,若不能加就加到父节点上。


写写写。。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <bits/stdc++.h>
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
	if (x <0) {
		putchar('-');
		x = -x;
	}
	if (x>9) pt(x / 10);
	putchar(x % 10 + '0');
}
using namespace std;
typedef long long ll;
#define N 10010
#define M 20010
struct Edge{
    int from,to;
    ll dis; int next;
    bool cut;
}edge[2*M];
int head[N],edgenum;
void init(){edgenum = 0; memset(head,-1,sizeof(head));}
int Low[N],DFN[N],Stack[N];//Belong数组的值是1~block
int Index,top;
int Belong[N],block, B_siz[N];//新图的连通块标号(1~block)
bool Instack[N];
int bridge; //割桥数量

void addedge(int u,int v, ll d){
    Edge E={u,v,d,head[u],0}; edge[edgenum]=E; head[u] = edgenum++;
    Edge E2={v,u,d,head[v],0};edge[edgenum]=E2;head[v] = edgenum++;
}
void Tarjan(int u,int pre){
    int v;
    Low[u] = DFN[u] = ++Index;
    Stack[top++] = u;
    Instack[u] = true;
    for(int i = head[u]; ~i ;i = edge[i].next){
        v = edge[i].to;
        if( v == pre )continue;
        if( !DFN[v] ){
            Tarjan(v,u);
            if(Low[u] > Low[v])Low[u] = Low[v];
            if(Low[v] > Low[u]){
                bridge++;
                edge[i].cut = true;
                edge[i^1].cut = true;
            }
        }
        else if(Instack[v] && Low[u] > DFN[v])Low[u] = DFN[v];
    }
    if(Low[u] == DFN[u]){
        block++;
        B_siz[block] = 0;
        do{
            v = Stack[--top];
            Instack[v] = false;
            Belong[v] = block;
            B_siz[block] ++;
        }while( v != u );
    }
}
void work(int l, int r){
    memset(DFN,0,sizeof(DFN));
    memset(Instack,false,sizeof(Instack));
    Index = top = block = bridge = 0;
    for(int i = l; i <= r; i++)if(!DFN[i])Tarjan(i,i);
}
vector<int>G[N];//点标从1-block
struct node{
    int from, to; ll d; int nex;
    int oldfrom, oldto;
    void put(){printf("(%d,%d) val:%lld, (%d,%d)\n", from, to, d, oldfrom, oldto);}
}e[2*M];
int H[N], em;
void add(int u,int v, ll d,int ou, int ov){
    node E= {u,v,d,H[u], ou, ov}; e[em]=E; H[u] = em++;
    node E2={v,u,d,H[v], ov, ou}; e[em]=E2;H[v] = em++;
}
void init_new(){memset(H, -1, sizeof H); em = 0;}
void suodian(){
    init_new();
    for(int i = 0; i < edgenum; i+=2){
        int u = Belong[edge[i].from], v = Belong[edge[i].to];
        if(u==v)continue;
        add(u,v,edge[i].dis, edge[i].from, edge[i].to);
    }
}
int siz[N], fa_id[N];
void find_siz(int u, int fa){
    siz[u] = B_siz[u];
    fa_id[u] = -1;
    for(int i = H[u]; ~i; i = e[i].nex){
        int v = e[i].to;
        if(v == fa)
        {
            fa_id[u] = i;
            continue;
        }
        find_siz(v, u);
        siz[u] += siz[v];
    }
}
bool vis[N];
void cal_n(int u, int fa,int tree_node){
    vis[u] = 1;
    for(int i = H[u]; ~i; i = e[i].nex){
        int v = e[i].to;
        if(v == fa)
        {
            e[i].d *= (ll)siz[u] * (ll)(tree_node-siz[u]);
            e[i^1].d = e[i].d;
            continue;
        }
        cal_n(v, u, tree_node);
    }
}

void work1(){
    memset(siz, 0, sizeof siz);
    for(int i = 1; i <= block; i++)
        if(siz[i] == 0)
        find_siz(i, i);
    memset(vis, 0, sizeof vis);
    for(int i = 1;  i <= block; i++)
        if(vis[i] == 0)
        cal_n(i,i,siz[i]);
}

int n, m;
ll dp[N], a[N], l, r, mid;
bool dfs(int u, int fa){
    vis[u] = 1;
    for(int i = H[u]; ~i; i = e[i].nex){
        int v = e[i].to;
        if(v == fa)
            continue;
        if(false == dfs(v, u))return false;
    }
    if(fa_id[u] != -1)
    {
        int ou = e[fa_id[u]].oldfrom, ov = e[fa_id[u]].oldto;
        if(dp[ou] + e[fa_id[u]].d <= mid)
            dp[ou] += e[fa_id[u]].d;
        else if(dp[ov] + e[fa_id[u]].d <= mid)
            dp[ov] += e[fa_id[u]].d;
        else return false;
    }
    return dp[u] <= mid;
}
bool ok(){
    for(int i = 1; i <= n; i++) dp[i] = a[i];
    memset(vis, 0, sizeof vis);
    for(int i = 1; i <= block; i++)
        if(vis[i] == 0)
        {
            if(dfs(i, i) == false)
                return false;
        }
    return true;
}
void input(){
    rd(n); rd(m);
    l = r = 0;
    for(int i = 1; i <= n; i++){ rd(a[i]); l = max(l, a[i]);}
    init();
    int u, v; ll d;
    while(m--){
        rd(u); rd(v); rd(d);
        addedge(u, v, d);
        r = max(r, d);
    }
    r *= (ll)n*(ll)n;
    r += l+1;
}

int main(){
    int T, Cas = 1; rd(T);
    while(T--){
        input();
        work(1, n);
        suodian();
        work1();
        ll ans = r;
        while(l <= r){
            mid = (l+r)>>1;
            if(ok()){
                r = mid-1;
                ans = min(ans, mid);
            }
            else
                l = mid+1;
        }
        printf("Case %d: ", Cas++);  pt(ans); puts("");
	}
	return 0;
}


你可能感兴趣的:(Uva 12587 Reduce the Maintenance Cost 边双连通+树形dp+贪心)