Description
在Byteland一共有n 个城市,编号依次为1 到n,它们之间计划修建m条双向道路,其中修建第i 条道路的费用为ci。
Byteasar作为Byteland 公路建设项目的总工程师,他决定选定一个区间[l, r],仅使用编号在该区间内的道路。他希望选择一些道路去修建,使得连通块的个数尽量少,同时,他不喜欢修建多余的道路,因此每个连通块都可以看成一棵树的结构。
为了选出最佳的区间,Byteasar 会不断选择q 个区间,请写一个程序,帮助Byteasar 计算每个区间内修建公路的最小总费用。
Data Constraint
Solution
这是个勇敢的做法(我在考场上怎就没想到这样维护呢!!!)
我们打一颗线段树维护当前区间[l,r]的边构成的最小生成树的边的编号。每次合并时用归并搞一下,在上Kruskal搞一下,合并一次O(N)。所以总复杂度O((m+q)nlogM)。
Code
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1e5+5;
struct code{
int x,y,z;
}f[maxn*4][101],a[maxn],b[maxn];
int n,m,p,i,t,j,k,l,x,y,z,num,ans,fa[maxn];
int getfather(int x){
if (x==fa[x]) return x;
fa[x]=getfather(fa[x]);return fa[x];
}
void make(int x,int y,int z){
int i=1,j=1,k,p,q;num=0;
while(f[y][i].x && f[z][j].y)
if (f[y][i].z<f[z][j].z) b[++num]=f[y][i++];
else b[++num]=f[z][j++];
while (f[y][i].x) b[++num]=f[y][i++];
while (f[z][j].x) b[++num]=f[z][j++];
for (k=1;k<=n;k++)fa[k]=k;
k=0;
for (i=1;i<=num;i++){
p=getfather(b[i].x);q=getfather(b[i].y);
if (p!=q) f[x][++k]=b[i],fa[p]=q;
}
}
void insert(int l,int r,int v){
int mid=(l+r)/2;
if (l==r){
f[v][1].x=a[l].y;f[v][1].z=a[l].z;f[v][1].y=a[l].x;return;
}
insert(l,mid,v*2);insert(mid+1,r,v*2+1);
make(v,v*2,v*2+1);
}
void find(int l,int r,int v,int x,int y){
int mid=(l+r)/2;
if (l>=x && r<=y){
make(0,v,0);return;
}
if (l<=y && mid>=x) find(l,mid,v*2,x,y);
if (mid<y && r>=x) find(mid+1,r,v*2+1,x,y);
}
int main(){
freopen("highway.in","r",stdin);freopen("highway.out","w",stdout);
scanf("%d%d%d",&n,&m,&p);
for (i=1;i<=m;i++)
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
insert(1,m,1);
for (i=1;i<=p;i++){
scanf("%d%d",&x,&y);
memset(f[0],0,sizeof(f[0]));
find(1,m,1,x,y);ans=0;j=1;
while (f[0][j].x) ans+=f[0][j++].z;
printf("%d\n",ans);
}
}