码迷,mamicode.com
首页 > 其他好文 > 详细

【Gym101137K】Knights of the Old Republic(生成树 DP)

时间:2019-10-30 22:23:27      阅读:130      评论:0      收藏:0      [点我收藏+]

标签:problem   math   通过   scanf   std   ref   ret   include   one   

题目链接

大意

给定N个点M条边的一张图,其中:
每个点有两个属性\(A_i,B_i\),表示你需要至少\(A_i\)个士兵来攻占该点,而空投一个士兵至该点需要Bi的花费。
每条边都有一个属性\(C_i\),表示如果该边的两个端点的士兵数量之和大于了\(C_i\),那么这条边就被打通了,即士兵可以自由通过该边。

求:攻占过所有点的最小代价。
\(1\le N \le 3\cdot 10^5\)

思路

首先,最小生成树经典算法Kruskal直接套,把边按值从小到大排序。

考虑一条边所连接的两个连通块如何合并。
\(Dp[i]\)表示点集\(i\)被攻占完的最下代价,则:
\(Dp[s+t]\)的值为Min(\(Dp[s]+Dp[t]\),打通这条边情况下的最小值)

贪心地想,若这两个连通块总共需要\(K\)个士兵,则这\(K\)个士兵一定是从这两个连通块中有着最小的\(A\)值的点空降的,这样可以满足代价最小。
同时,这样放也可以利用Kruskal的性质:之前放的边值一定都小于当前边值,所以如果要加入这条边,那么之前的边一定都会被打通。

综上:
先把所有边按边权排序,然后在不断合并两个连通块的同时,
动态维护连通块内的最小花费,最大需求与答案值。

出解。

代码

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=300005;
const long long ONE=1;
int N,M,Fa[MAXN];
int Mx[MAXN],Mi[MAXN];
struct Edge{int x,y,z;}s[MAXN];
bool cmp(Edge A,Edge B){return A.z<B.z;}
int Find(int x){return Fa[x]==x?x:Fa[x]=Find(Fa[x]);}
long long Ans,Dp[MAXN];
int main(){
    scanf("%d%d",&N,&M);
    for(int i=1,x,y;i<=N;i++){
        scanf("%d%d",&x,&y);
        Fa[i]=i;Dp[i]=ONE*x*y;
        Mi[i]=y;Mx[i]=x;
    }
    for(int i=1;i<=M;i++)
        scanf("%d%d%d",&s[i].x,&s[i].y,&s[i].z);
    sort(s+1,s+M+1,cmp);
    for(int i=1;i<=M;i++){
        int x=Find(s[i].x);
        int y=Find(s[i].y);
        if(x==y)continue;Fa[x]=y;
        Mi[y]=min(Mi[x],Mi[y]);
        Mx[y]=max(s[i].z,max(Mx[x],Mx[y]));
        Dp[y]=min(Dp[x]+Dp[y],ONE*Mi[y]*Mx[y]);
    }
    for(int i=1;i<=N;i++)
        if(Find(i)==i)Ans+=Dp[i];
    printf("%lld\n",Ans);
}

【Gym101137K】Knights of the Old Republic(生成树 DP)

标签:problem   math   通过   scanf   std   ref   ret   include   one   

原文地址:https://www.cnblogs.com/ftotl/p/11768252.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!