【jzoj5060】【GDOI2017第二轮模拟day1】【公路建设】【数据结构】

70 篇文章 0 订阅
22 篇文章 0 订阅

题目大意

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

解题思路

用线段树维护每个区间边的mst,归并合并,查询同理。

code

#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LD double
#define LL long long
#define ULL unsigned long long
#define min(a,b) ((a<b)?a:b)
#define max(a,b) ((a>b)?a:b)
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define fr(i,j) for(int i=begin[j];i;i=next[i])
using namespace std;
int const mn=100+9,mm=1e5+9,inf=1e9;
int n,m,q,cnt,fa[mn];
struct rec{
    int u,v,c;
};
rec a[mm];
struct rec2{
    int size,ans;
    rec a[101];
};
rec2 b[mm*4],d[3];
int get(int x){
    if(!fa[x])return x;
    return fa[x]=get(fa[x]);
}
void merge(rec2 &x,rec2 &y,rec2 &z){
    int i=1,j=1,k=0;d[0].ans=0;
    while((i<=x.size)||(j<=y.size)){
        if((j>y.size)||((i<=x.size)&&(x.a[i].c<y.a[j].c))){
            int fu=get(x.a[i].u),fv=get(x.a[i].v);
            if(fu!=fv){
                fa[fu]=fv;d[0].ans+=x.a[i].c;
                d[0].a[++k]=x.a[i];
            }
            i++;
        }else{
            int fu=get(y.a[j].u),fv=get(y.a[j].v);
            if(fu!=fv){
                fa[fu]=fv;d[0].ans+=y.a[j].c;
                d[0].a[++k]=y.a[j];
            }
            j++;
        }
    }
    d[0].size=k;
    z.ans=d[0].ans;z.size=d[0].size;
    fo(i,1,k)fa[d[0].a[i].u]=fa[d[0].a[i].v]=0,z.a[i]=d[0].a[i];
}
void build(int now,int l,int r){
    if(l==r){
        b[now].a[1]=a[l];
        b[now].size=1;
        b[now].ans=a[l].c;
        return;
    }
    int mid=(l+r)>>1,lson=now*2,rson=now*2+1;
    build(lson,l,mid);
    build(rson,mid+1,r);
    merge(b[lson],b[rson],b[now]);
}
void qury(int now,int l,int r,int u,int v){
    int mid=(l+r)>>1;
    if((l==u)&&(r==v)){
        d[++cnt].size=b[now].size;d[cnt].ans=b[now].ans;
        fo(i,1,d[cnt].size)d[cnt].a[i]=b[now].a[i];
        if(cnt==2){
            merge(d[1],d[2],d[1]);
            cnt=1;
        }
        return;
    }
    if(v<=mid)qury(now*2,l,mid,u,v);
    else if(mid<u)qury(now*2+1,mid+1,r,u,v);
    else{
        qury(now*2,l,mid,u,mid);
        qury(now*2+1,mid+1,r,mid+1,v);
    }
}
int main(){
    //freopen("highway.in","r",stdin);
    //freopen("highway.out","w",stdout);
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d%d%d",&n,&m,&q);
    fo(i,1,m)scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].c);
    build(1,1,m);int l,r;
    fo(cas,1,q){
        scanf("%d%d",&l,&r);
        if(cas==469){
            int bb;
            bb++;
        }
        qury(1,1,m,l,r);
        printf("%d\n",d[1].ans);cnt=0;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值