1. 程式人生 > >【題解】逐個擊破 luogu2700

【題解】逐個擊破 luogu2700

white cdt ldl 離開 ros print .aspx -h ftw

題目

題目描述:

現在有N個城市,其中K個被敵方軍團占領了,N個城市間有N-1條公路相連,破壞其中某條公路的代價是已知的。

現在,告訴你K個敵方軍團所在的城市,以及所有公路破壞的代價,請你算出花費最少的代價將這K個地方軍團互相隔離開,以便第二步逐個擊破敵人。

輸入格式

第一行包含兩個正整數n和k。

第二行包含k個整數,表示哪個城市別敵軍占領。

接下來n-1行,每行包含三個正整數a,b,c,表示從a城市到b城市有一條公路,以及破壞的代價c。城市的編號從0開始。

輸出格式

輸出一行一個整數,表示最少花費的代價。

輸入輸出樣例

輸入

5 3
1 2 4
1 0 4
1 3 8
2 1 1
2 4 3

輸出

4

思路

  • 貪心一下,使得兩個被占領的城市不連通,並不需要將中間的普通道路全部拆光,只要選取一些和占領點相連的邊。

  • 可以想到,如果一條邊和另一條邊相連了,那麽可以將兩條邊視為一條邊,同理推廣到所有的邊上,引出了並查集思想,當然也可以用 kruskal 來理解。

  • 那麽對於一條邊所處的並查集,如果裏面已經與一個聚集地相連,那麽這個並查集一
    定不能和另外一個包含與另外的聚集地相連的路徑的並查集合並,對於合法的路徑,每次加入的時候我們統計它的長度,因為路徑合法,所以不是必須拆掉的路。

  • 最後用總長度減去這些長度就可以得到拆路的最少代價。

  • 先將邊排序,保證選擇到必須拆的道路時一定是花費最少的


代碼

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

int n,m;
long long ans;
int vis[100010],fa[100010];
struct arr{
    int u,v,w;
}e[100010];

inline int read(){
    int x=0,w=1;char ch=0
; while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘)) ch=getchar(); if(ch==‘0‘) w=-1,ch=getchar(); while(ch>=‘0‘&&ch<=‘9‘) x=(x<<3)+(x<<1)+(ch-48),ch=getchar(); return x*w; } int gf(int x){ return x==fa[x]?x:fa[x]=gf(fa[x]);} inline int cmp(arr a,arr b){ return a.w>b.w;} int main(){ //freopen("shiitake.in","r",stdin); //freopen("shiitake.out","w",stdout); n=read();m=read(); for(int i=1;i<=m;i++){ int x=read();vis[x]=1; } for(int i=1;i<n;i++){ int u=read(),v=read(),w=read(); e[i].u=u;e[i].v=v;e[i].w=w; ans+=w; } sort(e+1,e+n,cmp); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<n;i++){ int f1=gf(e[i].u); int f2=gf(e[i].v); if(!(vis[f1]&&vis[f2])){ fa[f2]=f1; vis[f1]=(vis[f1]||vis[f2]); ans-=e[i].w; } } printf("%lld\n",ans); }

?

【題解】逐個擊破 luogu2700