动态最小生成树

题目详情

在这里插入图片描述

题目解析

动态最小生成树用线段树维护,线段树维护的区间的是所有的边,线段树的点维护一个数组,代表这一段边的区间组成的最小生成树用到了哪些边的编号,合并的过程类似归并排序,在两段的最小生成树边中挑,构造出新的最小生成树,记录所使用的边

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=210,M=30010;
struct Edge
{
    int a,b,c;
}e[M];
struct Node
{
    int id[N];
}tr[M*8];
int p[N];
int ans[N],tmp[N];
int n,m,q;
int find(int x)
{
    if(x!=p[x])return find(p[x]);
    return p[x];
}
void pushup(int root)
{
    for(int i=1;i<=n;i++)p[i]=i,tr[root].id[i]=0;
    int l=root*2,r=root*2+1;
    int pp=1,qq=1;
    for(int i=1;i<n;)
    {
        int pos1=tr[l].id[pp],pos2=tr[r].id[qq];
        if(!pos1&&!pos2)break;
        if(!pos1||(pos2&&e[pos1].c>=e[pos2].c))
        {
            int a=find(e[pos2].a);
            int b=find(e[pos2].b);
            if(a!=b)
            {
                tr[root].id[i]=pos2;
                p[a]=b;
                i++;
            }
            qq++;
        }
        else
        {
            int a=find(e[pos1].a);
            int b=find(e[pos1].b);
            if(a!=b)
            {
                tr[root].id[i]=pos1;
                p[a]=b;
                i++;
            }
            pp++;
        }
    }
}
void build(int u,int l,int r)
{
    if(l==r)
    {
        tr[u].id[1]=l;
        return ;
    }
    int mid=l+r>>1;
    build(u*2,l,mid);
    build(u*2+1,mid+1,r);
    pushup(u);
}
void update(int u,int l,int r,int x)
{
    if(l==r)return;
    int mid=l+r>>1;
    if(x<=mid)update(u*2,l,mid,x);
    else update(u*2+1,mid+1,r,x);
    pushup(u);
}
void solve(int root)
{
    for(int i=1;i<=n;i++)p[i]=i,tmp[i]=0;
    int pp=1,qq=1;
    for(int i=1;i<n;)
    {
        int pos1=tr[root].id[pp];
        int pos2=ans[qq];
        if(!pos1&&!pos2)break;
        if(!pos1||(pos2&&e[pos1].c>=e[pos2].c))
        {
            int a=find(e[pos2].a);
            int b=find(e[pos2].b);
            if(a!=b)
            {
                tmp[i]=pos2;
                p[a]=b;
                i++;
            }
            qq++;
        }
        else
        {
            int a=find(e[pos1].a);
            int b=find(e[pos1].b);
            if(a!=b)
            {
                tmp[i]=pos1;
                p[a]=b;
                i++;
            }
            pp++;
        }
    }
    for(int i=1;i<=n;i++)ans[i]=tmp[i];
}
void query(int u,int l,int r,int L,int R)
{
    if(l>=L&&r<=R)
    {
        solve(u);
        return ;
    }
    int mid=l+r>>1;
    if(L<=mid)query(u*2,l,mid,L,R);
    if(R>mid)query(u*2+1,mid+1,r,L,R);
}
int main()
{
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].c);
    }
    build(1,1,m);
    while(q--)
    {
        int op;
        scanf("%d",&op);
        if(op==1)
        {
            int x,y,z,t;
            scanf("%d%d%d%d",&x,&y,&z,&t);
            e[x].a=y,e[x].b=z,e[x].c=t;
            update(1,1,m,x);
        }
        else 
        {
            memset(ans,0,sizeof(ans));
            int l,r;
            scanf("%d%d",&l,&r);
            query(1,1,m,l,r);
            if(!ans[n-1])puts("Impossible");
            else
            {
            ll res=0;
            for(int i=1;i<=n;i++)res+=e[ans[i]].c;
            cout<<res<<endl;
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值