传送门biu~
用线段树维护使用编号在[l,r]内的道路最终会选择哪些道路。即线段树每个节点记录一个边的集合,集合大小显然不超过n。
在合并两个区间的时候采用Kruskal用归并的方式合并(暴力)。
#include<bits/stdc++.h>
#define N 105
#define M 100005
#define inf 0x3f3f3f3f
using namespace std;
struct Node{int l,r,an[N];}tree[M<<2];
struct Edge{int u,v,w;}e[M];
int n,m,T,fa[N],ans[N][N],cnt;
int search(int x){return fa[x]==x ? x : fa[x]=search(fa[x]);}
inline void Update(int re[],int x[],int y[]){
int now=0,lx=1,ly=1;
for(int i=1;i<=n;++i) fa[i]=i;
while(now<n && (x[lx] || y[ly])){
if(x[lx] && e[x[lx]].w<=e[y[ly]].w){
int fv=search(e[x[lx]].v),fu=search(e[x[lx]].u);
if(fv^fu) fa[fv]=fu,re[++now]=x[lx];
++lx;
}
else if(y[ly] && e[y[ly]].w<=e[x[lx]].w){
int fv=search(e[y[ly]].v),fu=search(e[y[ly]].u);
if(fv^fu) fa[fv]=fu,re[++now]=y[ly];
++ly;
}
}
while(now<n && re[now]) re[++now]=0;
}
void build(int num,int l,int r){
tree[num].l=l,tree[num].r=r;
if(l==r){
tree[num].an[1]=r;
return;
}
int mid=l+r>>1;
build(num<<1,l,mid),build(num<<1|1,mid+1,r);
Update(tree[num].an,tree[num<<1].an,tree[num<<1|1].an);
}
void Query(int num,int l,int r,int now){
if(l==tree[num].l && r==tree[num].r){
for(int i=1;i<n;++i) ans[now][i]=tree[num].an[i];
return;
}
int mid=tree[num].l+tree[num].r>>1,ls,rs;
if(mid>=r){
Query(num<<1,l,r,ls=++cnt);
for(int i=1;i<n;++i) ans[now][i]=ans[ls][i];
}
else if(mid<l){
Query(num<<1|1,l,r,rs=++cnt);
for(int i=1;i<n;++i) ans[now][i]=ans[rs][i];
}
else{
Query(num<<1,l,mid,ls=++cnt),Query(num<<1|1,mid+1,r,rs=++cnt);
Update(ans[now],ans[ls],ans[rs]);
}
}
int main(){
scanf("%d%d%d",&n,&m,&T);
for(int i=1;i<=m;++i) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
e[0].w=inf; build(1,1,m);
while(T--){
int l,r,Ans=0;
scanf("%d%d",&l,&r);
Query(1,l,r,cnt=1);
for(int i=1;ans[1][i];++i) Ans+=e[ans[1][i]].w;
printf("%d\n",Ans);
}
return 0;
}