• [BZOJ3206][Apio2013]道路费用


    [BZOJ3206][Apio2013]道路费用

    试题描述

    输入

    第一行包含三个由空格隔开的整数N,M和K。接下来的 M行描述最开始的M 条道路。这M行中的第i行包含由空格隔开的整数ai,bi和c i,表示有一条在a i和b i之间,费用为c i的双向道路。接下来的K行描述新建的K条道路。这 K行中的第i行包含由空格隔开的整数 xi和yi,表示有一条连接城镇xi和yi新道路。最后一行包含N个由空格隔开的整数,其中的第j个为pj,表示从城镇j 前往城镇 1的人数。输入也满足以下约束条件。1 ≤ N ≤ 100000;1 ≤ K ≤ 20;1 ≤ M ≤ 300000;对每个i和j,1 ≤ ci, pj ≤ 10^6;

    输出

    你的程序必须输出恰好一个整数到标准输出,表示能获得的最大的收入。

    输入示例

    5 5 1
    3 5 2
    1 2 3
    2 3 5
    2 4 4
    4 3 6
    1 3
    10 20 30 40 50

    输出示例

    400

    数据规模及约定

    见“输入

    题解

    先强制 K 条边都选,即把它们先都放到图里。那么现在如果图是不连通的,我们就需要按边权从小到大依次往里加入那 M 条边直到联通,那么这一步中我加入的边是所有方案中都必选的边。

    接下来,把图中的边清空,加入必选边,连通块缩成点,这样我们会得到一个点数不超过 K + 1 的图,令此图为新图(注意,新图中不包含任何边,只有那至多 K + 1 个点),同时 M 条边中必选边之外的边如果放在新图中会有很多重边,将这些重边合并,于是压缩成了最多 K2 条边。然后我们二进制枚举 K 条边是否选取,对于一个必选的属于 Mr.Greedy 的边的集合 S,在新图中加入集合 S 中的所有边,如果图不连通,我们就需要用那 K2 条边中的某些边连通整个图(令这些边为K2必选边);重新在新图中加入K2必选边,连通块缩点后加入集合 S 中的边我们就得到了一棵只包含 S 中的边的树;那么再依次找 K2 里非必选的边(记得重标号,因为刚刚又缩了一次点),对于一条这样的边 (u, v),在树上路径 (u, v) 上所有边的权值的最大值需要和这条边 (u, v) 的权值取 min。

    还是贴代码吧。。。。。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 100010
    #define maxk 25
    #define maxm 300010
    #define oo 2147483647
    #define LL long long
    
    int n, K, M, Peo[maxn];
    
    struct Edge {
    	int u, v, w;
    	Edge() {}
    	Edge(int _1, int _2, int _3): u(_1), v(_2), w(_3) {}
    	bool operator < (const Edge& t) const { return w < t.w; }
    } es[maxm], ek_own[maxk], ek[maxk*maxk];
    bool used[maxm];
    int K_id[maxn], G[maxk][maxk];
    LL siz[maxk], reas[maxk];
    
    int fa[maxn];
    int findset(int x) { return x == fa[x] ? x : fa[x] = findset(fa[x]); }
    
    int m, head[maxk], nxt[maxk<<1], to[maxk<<1], eid[maxk<<1];
    void AddEdge(int a, int b, int c) {
    	to[++m] = b; eid[m] = c; nxt[m] = head[a]; head[a] = m;
    	swap(a, b);
    	to[++m] = b; eid[m] = c; nxt[m] = head[a]; head[a] = m;
    	return ;
    }
    
    int S[maxk], top;
    bool dfs(int u, int pa, int s, int t, int w) {
    	if(u == t) {
    		int U = findset(s);
    		for(int i = 1; i <= top; i++) {
    			ek_own[eid[S[i]]].w = min(ek_own[eid[S[i]]].w, w);
    //			printf("update %d: %d
    ", eid[S[i]], w);
    			s = to[S[i]];
    			fa[findset(s)] = U;
    		}
    		return 1;
    	}
    	for(int e = head[u]; e; e = nxt[e]) if(to[e] != pa) {
    		S[++top] = e;
    		if(dfs(to[e], u, s, t, w)) return 1;
    		top--;
    	}
    	return 0;
    }
    
    LL trs[maxk], ans, tmp;
    void build(int u, int pa) {
    	trs[u] = reas[u];
    	for(int e = head[u]; e; e = nxt[e]) if(to[e] != pa) {
    		build(to[e], u);
    		trs[u] += trs[to[e]];
    //		printf("trs: %lld
    ", trs[to[e]]);
    		tmp += trs[to[e]] * ek_own[eid[e]].w;
    	}
    	return ;
    }
    
    int main() {
    //	freopen("data.in", "r", stdin);
    	n = read(); M = read(); K = read();
    	for(int i = 1; i <= M; i++) {
    		int a = read(), b = read(), c = read();
    		es[i] = Edge(a, b, c);
    	}
    	for(int i = 1; i <= n; i++) fa[i] = i;
    	for(int i = 1; i <= K; i++) {
    		int a = read(), b = read();
    		ek_own[i] = Edge(a, b, oo);
    		int U = findset(a), V = findset(b);
    		if(U != V) fa[V] = U;
    	}
    	for(int i = 1; i <= n; i++) Peo[i] = read();
    	
    	sort(es + 1, es + M + 1);
    	for(int i = 1; i <= M; i++) {
    		Edge& e = es[i];
    		int U = findset(e.u), V = findset(e.v);
    		if(U != V) used[i] = 1, fa[V] = U;
    	}
    	for(int i = 1; i <= n; i++) fa[i] = i;
    	for(int i = 1; i <= M; i++) if(used[i]) fa[findset(es[i].v)] = findset(es[i].u);
    	// above: get K connected_block
    	int cntk = 0;
    	for(int i = 1; i <= n; i++) {
    		int u = findset(i);
    		if(!K_id[u]) K_id[u] = ++cntk;
    		siz[K_id[i] = K_id[u]] += Peo[i];
    	}
    	// above: get connected_block's id
    	for(int i = 1; i <= K; i++) {
    		Edge& e = ek_own[i];
    		e.u = K_id[e.u]; e.v = K_id[e.v];
    	}
    	for(int i = 1; i <= cntk; i++)
    		for(int j = 1; j <= cntk; j++) G[i][j] = oo;
    	for(int i = 1; i <= M; i++) if(!used[i]) {
    		Edge& e = es[i];
    		int u = K_id[e.u], v = K_id[e.v];
    		G[u][v] = G[v][u] = min(G[u][v], e.w);
    	}
    	int cek = 0;
    	for(int i = 1; i <= cntk; i++)
    		for(int j = i + 1; j <= cntk; j++) if(G[i][j] < oo)
    			ek[++cek] = Edge(i, j, G[i][j]);
    	sort(ek + 1, ek + cek + 1);
    	// above: get prepared, ek means edges which don't belong to Mr.Greedy
    	/*for(int i = 1; i <= n; i++) printf("%d%c", K_id[i], i < n ? ' ' : '
    ');
    	for(int i = 1; i <= cntk; i++) printf("%lld%c", siz[i], i < cntk ? ' ' : '
    ');
    	for(int i = 1; i <= cek; i++) printf("%d %d: %d
    ", ek[i].u, ek[i].v, ek[i].w); // */
    	
    	int all = (1 << K) - 1;
    	for(int S = 0; S <= all; S++) {
    //		printf("here %d %d %d
    ", cntk, K, cek);
    		for(int i = 1; i <= cntk; i++) fa[i] = i;
    		bool ok = 1;
    		for(int i = 1; i <= K; i++) if(S >> i - 1 & 1) {
    			Edge& e = ek_own[i];
    			int U = findset(e.u), V = findset(e.v);
    			if(U == V){ ok = 0; break; }
    			fa[V] = U;
    		}
    		if(!ok) continue;
    		for(int i = 1; i <= cek; i++) used[i] = 0;
    		for(int i = 1; i <= cek; i++) {
    			Edge& e = ek[i];
    			int U = findset(e.u), V = findset(e.v);
    			if(U != V) used[i] = 1, fa[V] = U;
    		}
    		for(int i = 1; i <= cntk; i++) fa[i] = i;
    		for(int i = 1; i <= cek; i++) if(used[i]) fa[findset(ek[i].v)] = findset(ek[i].u);
    		int cntn = 0;
    		for(int i = 1; i <= cntk; i++) K_id[i] = reas[i] = 0;
    		for(int i = 1; i <= cntk; i++) {
    			int u = findset(i);
    			if(!K_id[u]) K_id[u] = ++cntn;
    			reas[K_id[i] = K_id[u]] += siz[i];
    		}
    //		printf("trs: "); for(int i = 1; i <= cntn; i++) printf("%lld%c", reas[i], i < cntn ? ' ' : '
    ');
    		
    		m = 0; memset(head, 0, sizeof(head));
    		for(int i = 1; i <= K; i++) if(S >> i - 1 & 1) {
    			Edge& e = ek_own[i]; e.w = oo;
    			AddEdge(K_id[e.u], K_id[e.v], i);
    		}
    //		printf("here2 %d %d
    ", cntn, m);
    		for(int i = 1; i <= cntn; i++) fa[i] = i;
    		for(int i = 1; i <= cek; i++) if(!used[i]) {
    			Edge e = Edge(K_id[ek[i].u], K_id[ek[i].v], ek[i].w);
    			int U = findset(e.u), V = findset(e.v);
    			if(U != V) top = 0, dfs(e.u, 0, e.u, e.v, e.w);
    		}
    		
    		tmp = 0;
    		build(1, 0);
    //		printf("tmp: %lld
    ", tmp);
    		ans = max(ans, tmp);
    	}
    	
    	printf("%lld
    ", ans);
    	
    	return 0;
    }
    

    我都要写吐了。。。。。

  • 相关阅读:
    理解OAuth 2.0
    RESTful API 设计指南
    揭秘淘宝286亿海量图片存储与处理架构
    Windows+Nginx+IIS做图片分布式存储详细步骤
    MongoDB GridFS最佳应用概述
    Qt 槽函数的使用
    Qt pro文件语法
    CentOS6.2下Qt5.1.0无法输入中文
    CentOS6.2下安装中文输入法
    CentOS6.2下安装Qt5.1.0
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/6814959.html
Copyright © 2020-2023  润新知