【省选专题一】图论 jzoj 5060.【GDOI2017第二轮模拟day1】公路建设 线段树+最小生成树+并查集

Description

在Byteland一共有n 个城市,编号依次为1 到n,它们之间计划修建m条双向道路,其中修建第i 条道路的费用为ci。
Byteasar作为Byteland 公路建设项目的总工程师,他决定选定一个区间[l, r],仅使用编号在该区间内的道路。他希望选择一些道路去修建,使得连通块的个数尽量少,同时,他不喜欢修建多余的道路,因此每个连通块都可以看成一棵树的结构。
为了选出最佳的区间,Byteasar 会不断选择q 个区间,请写一个程序,帮助Byteasar 计算每个区间内修建公路的最小总费用。

Input

第一行包含三个正整数n, m, q,表示城市数、道路数和询问数。
接下来m 行,每行三个正整数ui,vi,ci,表示一条连接城市ui 和vi 的双向道路,费用为ci。
接下来q 行,每行两个正整数li, ri,表示一个询问。

Output

输出q 行,每行一个整数,即最小总费用。

Sample Input

3 5 2
1 3 2
2 3 1
2 1 6
3 1 7
2 3 7
2 5
3 4

Sample Output

7
13

Data Constraint
这里写图片描述

分析:我们建一棵线段树维护[l,r]的答案,显然对于一个段,最多有n-1条边,我们可以使用归并合并两个区间(两个区间都是有序的,这个用归并可以做出来,然后按从小到大插入边,做最小生成树)。空间大得可以,时间也是卡过= =

代码:

type
 node=record
 x,y,w:longint;
end;
 arr=array [1..102] of node;
var
 n,m,test,x,y,numa,sum,i,j:longint;
 tree:array [1..300001] of arr;
 num:array [1..300001] of longint;
 a:array [1..100001] of node;
 p:array [1..101] of longint;
 ans,b:arr;

function find(x:longint):longint;
 var root,w,y:longint;
begin
 y:=x;
 while p[y]>0 do y:=p[y];
 root:=y;
 y:=x;
 while p[y]>0 do
  begin
   w:=p[y];
   p[y]:=root;
   y:=w;
  end;
 exit(root);
end;

procedure union(x,y:longint);
 var u,v:longint;
begin
 u:=find(x);
 v:=find(y);
 if u<>v then p[v]:=u;
end;

procedure merge(x:arr;numx:longint; var y:arr; var numy:longint);
 var i,j,c:longint;
begin
 for i:=1 to n do
  p[i]:=0;
 i:=1; j:=1; c:=0;
 while (i<=numx) or (j<=numy) do
  begin
   while (i<=numx) and (find(x[i].x)=find(x[i].y)) do inc(i);
   while (j<=numy) and (find(y[j].x)=find(y[j].y)) do inc(j);
   if (i>numx) and (j>numy) then break;
   if ((j>numy) or (x[i].w<y[j].w)) and (i<=numx) then
    begin
     inc(c);
     b[c]:=x[i];
     union(x[i].x,x[i].y);
     inc(i);
    end
   else
    begin
     inc(c);
     b[c]:=y[j];
     union(y[j].x,y[j].y);
     inc(j);
    end;
   if c>=n-1 then break;
  end;
 numy:=c;
 for i:=1 to c do
  y[i]:=b[i];
end;

procedure build_tree(p,l,r:longint);
 var mid:longint;
begin
 if (l=r) then
  begin
   num[p]:=1;
   tree[p,1]:=a[l];
   exit;
  end;
 mid:=(l+r) shr 1;
 build_tree(p*2,l,mid);
 build_tree(p*2+1,mid+1,r);
 tree[p]:=tree[p*2];
 num[p]:=num[p*2];
 merge(tree[p*2+1],num[p*2+1],tree[p],num[p]);
end;

procedure count(p,l,r,s,t:longint);
 var mid:longint;
begin
 if (l=s) and (r=t) then
  begin
   merge(tree[p],num[p],ans,numa);
   exit;
  end;
 mid:=(l+r) shr 1;
 if t<=mid then count(p*2,l,mid,s,t)
  else
   if s>=mid+1 then count(p*2+1,mid+1,r,s,t)
    else
     begin
      count(p*2,l,mid,s,mid);
      count(p*2+1,mid+1,r,mid+1,t);
     end;
end;


begin
 assign(input,'highway.in');
 assign(output,'highway.out');
 reset(input);
 rewrite(output);
 readln(n,m,test);
 for i:=1 to m do
  readln(a[i].x,a[i].y,a[i].w);
 build_tree(1,1,m);
 for i:=1 to test do
  begin
   readln(x,y);
   numa:=0;
   fillchar(ans,sizeof(ans),0);
   count(1,1,m,x,y);
   sum:=0;
   for j:=1 to numa do
    sum:=sum+ans[j].w;
   writeln(sum);
  end;
 close(input);
 close(output);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值