zoukankan      html  css  js  c++  java
  • @gym


    @description@

    给定 N 个点 M 条边的一张图。
    每个点有两个属性 Ai, Bi,表示你需要至少 Ai 个士兵来攻占该点,向 i 点投放一个士兵需要 Bi 的花费。
    每条边都有一个属性 Ci,表示如果该边的两个端点的士兵数量之和 >= Ci,那么这条边就被打通了(即士兵可以自由通过该边)。

    士兵不会死亡。求攻占所有点的最小代价和。

    Input
    第一行包含两个整数 n, m (1 ≤ n,m ≤ 300000)。
    接下来 n 行,每行两个整数 ai 与 bi (0 ≤ ai,bi ≤ 1000000)。
    接下来 m 行,每行三个整数 si, fi 与 ci 描述一条边 (si, fi) (1 ≤ si,fi ≤ n, 0 ≤ ci ≤ 1000000)。

    Output
    输出一个整数,表示最小花费。

    Example
    standard input
    3 2
    10 5
    20 10
    10 3
    1 2 22
    2 3 200
    standard output
    140

    @solution@

    最终局面下,对于一个连通块,它对答案的贡献应为 min{bi} * max{max{ai}, max{cj}}。
    因为我至少要投放 max{max{ai}, max{cj}} 这么多士兵才能保证连通且攻占全部点,那不如直接投放这个连通块费用最小的点那里。

    当连通块对应的点集不变时,max{cj} 应尽量小才能获得更优的答案。
    在 max{cj} 最小的情况下连通这个连通块,这显然就是最小生成树了(参考 kruskal 的算法过程)。

    继续分析。当一个连通块的 max{ai, cj} 大于它邻接的某条边 k 的 ck 时,我加入 k 这条边显然不会更劣(max{ai, cj} 不变,min{b} 反而可能变小)。

    还可以得到一个更严格的限制:当一个连通块的 max{cj} 大于它邻接的某条边 k 的 ck 时,加入 k 这条边依然不会更劣。
    正确性,简单来说,就是 A*B + C*D >= min(A, C)*max(B, D)。假如 B <= D,则 min(A, C)*max(B, D) = min(A, C)*D <= C*D <= A*B + C*D。

    那么考虑 ci 最大的那一条边。假如它被选中,根据上面的理论,其他边也会被选中。如果选中其他边产生了环,证明它不是一棵最小生成树,显然不合法。否则,我们可以算出此时的答案。
    假如它不被选中,相当于求去除了这条边的最优值。这又递归成一个子问题。

    但是这个太慢了。不如反过来,考虑 ci 从小到大枚举。
    假如此时加入这条边,产生了环,与最小生成树矛盾,此时直接忽视第 i 条边(类 kruskal 的过程)。
    否则,计算这条边连接的两个点集的 min{b}, max{a}。此时因为 i 的 ci 是最大的,所以直接取 max{ci, max{a}} 即可。

    为了与不加入第 i 条边的最优值对比,我们对于每个点集再维护个 f,表示加入前 i-1 条边该点集的最优答案是什么。

    @accepted code@

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int MAXN = 300000;
    struct edge{
    	int u, v; ll w;
    	edge(int _u=0, int _v=0, ll _w=0) : u(_u), v(_v), w(_w) {}
    	friend bool operator < (edge a, edge b) {
    		return a.w < b.w;
    	}
    }e[MAXN + 5];
    int fa[MAXN + 5];
    ll f[MAXN + 5], ma[MAXN + 5], mb[MAXN + 5];
    int find(int x) {
    	return fa[x] = (fa[x] == x ? x : find(fa[x]));
    }
    void unite(int x, int y, ll w) {
    	int fx = find(x), fy = find(y);
    	if( fx != fy ) {
    		mb[fy] = min(mb[fy], mb[fx]);
    		ma[fy] = max(ma[fy], ma[fx]);
    		f[fy] = min(f[fx] + f[fy], max(ma[fy], w)*mb[fy]);
    		fa[fx] = fy;
    	}
    }
    int main() {
    	int n, m; scanf("%d%d", &n, &m);
    	for(int i=1;i<=n;i++) scanf("%lld%lld", &ma[i], &mb[i]);
    	for(int i=1;i<=m;i++) scanf("%d%d%lld", &e[i].u, &e[i].v, &e[i].w);
    	for(int i=1;i<=n;i++) fa[i] = i, f[i] = ma[i]*mb[i];
    	sort(e + 1, e + m + 1);
    	for(int i=1;i<=m;i++) unite(e[i].u, e[i].v, e[i].w);
    	ll ans = 0;
    	for(int i=1;i<=n;i++)
    		if( find(i) == i ) ans += f[i];
    	printf("%lld
    ", ans);
    }
    

    @details@

    感觉还是比较巧妙的题,运用了比较多的性质,写出来代码也不是很长。
    (我没学过图论.jpg)

  • 相关阅读:
    NOIP2008双栈排序[二分图染色|栈|DP]
    洛谷P1108 低价购买[DP | LIS方案数]
    洛谷P1330封锁阳光大学[二分图染色]
    NOIP模板整理计划
    期中考试
    UVA 10564 Paths through the Hourglass[DP 打印]
    UVA 11404 Palindromic Subsequence[DP LCS 打印]
    POJ2479 Maximum sum[DP|最大子段和]
    POJ3160 Father Christmas flymouse[强连通分量 缩点 DP]
    UVA11324 The Largest Clique[强连通分量 缩点 DP]
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11768046.html
Copyright © 2011-2022 走看看