【JZOJ5060】【GDOI2017第二轮模拟day1】公路建设

55 篇文章 0 订阅
14 篇文章 0 订阅

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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值