bzoj3995: [SDOI2015]道路修建【线段树维护最小生成树】

Description

某国有2N个城市,这2N个城市构成了一个2行N列的方格网。现在该国政府有一个旅游发展计划,这个计划需要选定L、R两列(L<=R),修建若干条专用道路,使得这两列之间(包括这两列)的所有2(R-L+1)个城市中每个城市可以只通过专用道路就可以到达这2(R-L+1)个城市中的任何一个城市。这种专用道路只能在同一行相邻两列的城市或者同一列的两个城市之间修建,且修建需要花费一定的费用。由于该国政府决定尽量缩减开支,因此政府决定,选定L、R后,只修建2(R-L+1)-1条专用道路,使得这些专用道路构成一个树结构。现在你需要帮助该国政府写一个程序,完成这个任务。具体地,该任务包含M个操作,每个操作的格式如下:
1. C x0 y0 x1 y1 w:由于重新对第x0行第y0列的城市和第x1行第y1列的城市之间的情况进行了考察,它们之间修建一条专用道路的花费变成了w;
2. Q L R:若政府选定的两列分别为L、R,询问政府的最小开支。

Input

第一行,两个整数N、M。

第二行,N-1个整数,其中第i个整数表示初始时第1行第i列的城市和第1行第i+1列的城市之间修建一条专用道路的费用。
第三行,N-1个整数,其中第i个整数表示初始时第2行第i列的城市和第2行第i+1列的城市之间修建一条专用道路的费用。
第四行,N个整数,其中第i个整数表示初始时第1行第i列的城市和第2行第i列的城市之间修建一条专用道路的费用。
接下来的M行,每行一个操作。

Output

对于每个询问操作,输出一行,表示你计算出的政府的最小开支。

Sample Input

3 3

1 2

2 1

3 1 2

Q 1 3

C 1 2 2 2 3

Q 2 3

Sample Output

7

5

HINT

对于全部的数据,1<=N, M<=60000,任何时刻任何一条专用道路的修建费用不超过10^4。

解题思路:

又是2*n的网格图连通性问题,类似于bzoj1018(可以去看看找思路,题解在这里),直接上线段树。
看了网上的题解,好像分类讨论很复杂,其实不用。
注意这道题与bzoj1018的不同,只会走(l,r)中的边,所以似乎要简单一些。

假设我们已经维护了左右子区间内的MST信息,如何合并更新当前区间的MST呢?
其实跟次小生成树的做法很像。
首先我们先连上(mid,mid+1)的两条边,这时左区间MST最右边的竖边(设为l.rp),右区间MST最左边的竖边(设为r.lp)与它们之间的横边一定会形成一个环,所以一定会在这个环上断掉一条最大的边就是当前区间的MST。
这样一来做法就比较明了了,每个节点维护当前MST值val,最左最右竖边位置lp,rp,区间内横边最大值mx即可。

细节可以参考我的代码(update有点丑,还套了一个query横边最大值,好在不会影响复杂度)。

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=60005;
const int dx[3]={-1,0,1};
const int dy[3]={0,1,0};
struct node{int l,r,lp,rp,val,mx,id;}tr[N<<2];
int n,m,a[N][2][2],r1,r2,c1,c2,w;
char s[5];

int querymx(int k,int x,int y)
{
    if(x>y)return 0;
    if(tr[k].l==x&&tr[k].r==y)return tr[k].mx;
    int mid=tr[k].l+tr[k].r>>1;
    if(y<=mid)return querymx(k<<1,x,y);
    else if(x>mid)return querymx(k<<1|1,x,y);
    else return max(querymx(k<<1,x,mid),querymx(k<<1|1,mid+1,y));
}

void update(node &u,node l,node r,int mid)
{
    u.l=l.l,u.r=r.r;
    u.val=l.val+r.val+a[mid][0][0]+a[mid][1][0];
    u.mx=max(max(l.mx,r.mx),max(a[mid][0][0],a[mid][1][0]));
    int mx=max(querymx(l.id,l.rp,mid),querymx(r.id,mid+1,r.lp-1)),tmp=max(a[l.rp][0][1],a[r.lp][0][1]);   
    if(tmp<=mx)u.val-=mx,u.lp=l.lp,u.rp=r.rp;
    else if(tmp==a[l.rp][0][1])u.val-=tmp,u.lp=(l.lp==l.rp?r.lp:l.lp),u.rp=r.rp;
    else u.val-=tmp,u.lp=l.lp,u.rp=(r.rp==r.lp?l.rp:r.rp);
}

void build(int k,int l,int r)
{
    tr[k].l=l,tr[k].r=r,tr[k].id=k;
    if(l==r)
    {
        tr[k].mx=max(a[l][0][0],a[l][1][0]);
        tr[k].lp=tr[k].rp=l,tr[k].val=a[l][0][1];
        return;
    }
    int mid=l+r>>1;
    build(k<<1,l,mid),build(k<<1|1,mid+1,r);
    update(tr[k],tr[k<<1],tr[k<<1|1],mid);
}

void modify(int k,int x)
{
    if(tr[k].l==tr[k].r)
    {
        tr[k].mx=tr[k].mx=max(a[tr[k].l][0][0],a[tr[k].l][1][0]);
        tr[k].val=a[tr[k].l][0][1];
        return;
    }
    int mid=tr[k].l+tr[k].r>>1;
    if(x<=mid)modify(k<<1,x);
    else modify(k<<1|1,x);
    update(tr[k],tr[k<<1],tr[k<<1|1],mid);
}

node query(int k,int x,int y)
{
    if(tr[k].l==x&&tr[k].r==y)return tr[k];
    int mid=tr[k].l+tr[k].r>>1;
    if(y<=mid)return query(k<<1,x,y);
    else if(x>mid)return query(k<<1|1,x,y);
    node l=query(k<<1,x,mid),r=query(k<<1|1,mid+1,y),res;
    l.id=k<<1,r.id=k<<1|1,res.id=k;
    update(res,l,r,mid);return res;
}

int main()
{
    //freopen("lx.in","r",stdin);
    n=getint(),m=getint();
    for(int i=1;i<n;i++)a[i][0][0]=getint();
    for(int i=1;i<n;i++)a[i][1][0]=getint();
    for(int i=1;i<=n;i++)a[i][0][1]=getint();
    build(1,1,n);
    while(m--)
    {
        scanf("%s",s);
        if(s[0]=='C')
        {
            r1=getint()-1,c1=getint(),r2=getint()-1,c2=getint(),w=getint();
            if(r1>r2)swap(r1,r2),swap(c1,c2);int i;
            for(i=0;i<3;i++)if(c1+dx[i]==c2&&r1+dy[i]==r2)break;
            if(i==0)a[c2][r2][0]=w,modify(1,c2);
            else if(i==1)a[c1][0][1]=w,modify(1,c1);
            else a[c1][r1][0]=w,modify(1,c1);
        }
        else 
        {
            c1=getint(),c2=getint();
            node ans=query(1,c1,c2);
            printf("%d\n",ans.val);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值