原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=5216
在Byteland一共有n个城市,编号依次为1到n,它们之间计划修建m条双向道路,其中修建第i条道路的费用为ci。Byteasar作为Byteland公路建设项目的总工程师,他决定选定一个区间[l,r],仅使用编号在该区间内的道路。他希望选择一些道路去修建,使得连通块的个数尽量少,同时,他不喜欢修建多余的道路,因此每个连通块都可以看成一棵树的结构。为了选出最佳的区间, Byteasar会不断选择 q个区间,请写一个程序,帮助 Byteasar计算每个区
间内修建公路的最小总费用。
第一行包含三个正整数n; m; q,表示城市数、道路数和询问数。
接下来m 行,每行三个正整数ui; vi; ci,表示一条连接城市ui 和vi 的双向道路,费用为ci。
接下来q 行,每行两个正整数li; ri,表示一个询问。
1 ≤ ui, vi ≤ n, ui ̸= vi, 1 ≤ li ≤ ri ≤ m, 1 ≤ ci ≤ 10^6
N<=100,M<=100000,Q<=15000
输出q 行,每行一个整数,即最小总费用。
3 5 2
1 3 2
2 3 1
2 1 6
3 1 7
2 3 7
2 5
3 4
7
13
暴力就是把区间内的边直接扔进 K r u s c a l \mathcal{Kruscal} Kruscal里做最小生成树。
发现 n = 100 n=100 n=100,出题人肯定在暗示些什么,考虑最后生成树森林里的边只有 n − 1 n-1 n−1条,那么我们不如开个线段树吧,每个节点维护管辖区间内做生成树可能用到的边,合并节点信息的时候做个归并就行。
#include
#define ls v<<1
#define rs ls|1
using namespace std;
const int N=105,M=1e5+5;;
int mst[M<<2][N],ans[N],tmp[N],f[N],n,m,q;
struct sd{int a,b,w;}ed[M];
int root(int v){return f[v]==v?v:f[v]=root(f[v]);}
void up(int *r,int *le,int *ri)
{
for(int i=1;i<=n;++i)f[i]=i;int k=0;
for(int i=1,j=1,fa,fb;i<=le[0]||j<=ri[0];)
{
if(i<=le[0]&&(j>ri[0]||ed[le[i]].w<ed[ri[j]].w)){if((fa=root(ed[le[i]].a))!=(fb=root(ed[le[i]].b)))f[fa]=fb,r[++k]=le[i];++i;}
else{if((fa=root(ed[ri[j]].a))!=(fb=root(ed[ri[j]].b)))f[fa]=fb,r[++k]=ri[j];++j;}
}
r[0]=k;
}
void build(int v,int le,int ri)
{
if(le==ri){mst[v][++mst[v][0]]=le;return;}
int mid=le+ri>>1;build(ls,le,mid);build(rs,mid+1,ri);
up(mst[v],mst[ls],mst[rs]);
}
void ask(int v,int le,int ri,int lb,int rb)
{
if(lb<=le&&ri<=rb){up(tmp,ans,mst[v]);for(int i=0;i<=tmp[0];++i)ans[i]=tmp[i];return;}
int mid=le+ri>>1;if(lb<=mid)ask(ls,le,mid,lb,rb);if(mid<rb)ask(rs,mid+1,ri,lb,rb);
}
int kruscal(int le,int ri)
{
ans[0]=0;ask(1,1,m,le,ri);int r=0;
for(int i=1;i<=n;++i)f[i]=i;
for(int i=1,fa,fb;i<=ans[0];++i)if((fa=root(ed[ans[i]].a))!=(fb=root(ed[ans[i]].b)))r+=ed[ans[i]].w,f[fa]=fb;
return r;
}
void in(){scanf("%d%d%d",&n,&m,&q);for(int i=1;i<=m;++i)scanf("%d%d%d",&ed[i].a,&ed[i].b,&ed[i].w);}
void ac(){build(1,1,m);for(int i=1,l,r;i<=q;++i)scanf("%d%d",&l,&r),printf("%d\n",kruscal(l,r));}
int main(){in();ac();}