1. 程式人生 > >2016 NEERC, Moscow Subregional Contest K. Knights of the Old Republic(Kruskal思想)

2016 NEERC, Moscow Subregional Contest K. Knights of the Old Republic(Kruskal思想)

std rep esp 一個點 include public fir sca code

2016 NEERC, Moscow Subregional Contest K. Knights of the Old Republic

題意:有一張圖,第i個點被占領需要ai個兵,而每個兵傳送至該點需要bi的費用。占領第i條邊需要其兩端點的兵數之和大等於ci。對於已占領的點或邊可以免費通行。因此兵達到一個點的手段有傳送和沿路走。圖上的兵可以在已占領的點、邊隨意調度。
求占領所有點的最小花費。
思路:將邊按ci進行升序排列,對於每條邊兩端點所在的連通塊進行合並,合並細節見代碼。這裏有一點值得思考:當你想打通當前這條邊時,由於排了序,你總可以默認之前那些ci小的邊已打通(因為兵可以調度)。因此,我們需要用並查集維護連通塊,以及每個連通塊中最小的傳送單價,最大的單點兵需求量,來更新花費。

#include<iostream>
#include<cstdio>
#include<algorithm>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,pair<int,int> > P;
const int maxn=3e5+10;
P edge[maxn];
int fa[maxn];
ll a[maxn],b[maxn],cost[maxn];
void init(int n)
{
  for (int i=1;i<=n;++i)
  {
    fa[i]=i;
    cost[i]=a[i]*b[i];
  }
}
int find(int x)
{
  return fa[x]==x?x:fa[x]=find(fa[x]);
}
void unio(int x,int y,ll c)
{
  int fx=find(x),fy=find(y);
  if (fx==fy)
    return;
  fa[fx]=fy;
  a[fy]=max(c,max(a[fy],a[fx]));
  b[fy]=min(b[fy],b[fx]);
  cost[fy]=min(cost[fy]+cost[fx],a[fy]*b[fy]);
}
int main()
{
  int n,m;
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;++i)
    scanf("%lld%lld",&a[i],&b[i]);
  for (int i=0;i<m;++i)
    scanf("%d%d%lld",&edge[i].se.fi,&edge[i].se.se,&edge[i].fi);
  sort(edge,edge+m);
  init(n);
  for (int i=0;i<m;++i)
  {
    int u=edge[i].se.fi,v=edge[i].se.se,c=edge[i].fi;
    unio(u,v,c);
  }
  ll ans=0;
  for (int i=1;i<=n;++i)
    if (fa[i]==i)
      ans+=cost[i];
  printf("%lld",ans);
  return 0;
}

2016 NEERC, Moscow Subregional Contest K. Knights of the Old Republic(Kruskal思想)