zoukankan      html  css  js  c++  java
  • [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。

    Source

    Round 1 感谢yts1999上传

    Solution

    这题就是一个(2*n)的网格图,求区间最小生成树,支持单边修改。

    我会kruskal!然而并没有什么用

    考虑这样的图的最小生成树长什么样,以及是否存在可合并性。

    显而易见的是一定至少有一条竖边。

    再接着分析,如果要合并两个相邻但不连通的最小生成树,我们有一个非常好的办法:

    先连接上下两条边,这时候一定能够形成一个矩形形状的环,我们在这个环中找到最大的元素,并且删去,这样就完成了相邻的最小生成树的合并。

    所以我们用线段树的话,每个节点需要维护的信息如下:

    左右端点,最左最右竖边的权值,以及最左最右竖边向左(右)延伸到尽头的边最大值(类似一个三面半包围结构),所有横边的最大值,答案,竖边的个数。

    合并的时候,左右端点,所有横边的最大值,答案,竖边个数可以直接求出。但是有需要特判的地方:

    如果某一边的竖边只有一个,那么新的左右竖边权值,左右竖边左右延伸最大值都要根据情况去变化。

    千万要写else!!不然两边权值相等并且两边各只有一个竖边的话会发生奇怪的错误。

    其实代码并不难写只是细节有点多

    Code

    #include <bits/stdc++.h>
    using namespace std;
    #define N 60005
    #define mid ((l+r)>>1)
    int a,b,c,d,e,n,m,v[N][3];char o[5];
    struct nod{int l,r,lm,rm,ls,rs,am,ans,cnt;void init(int x){lm=rm=ls=rs=ans=v[x][2];cnt=1;am=0;}}s[N*4];
    nod merge(nod x,nod y)
    {
        nod z;z.l=x.l;z.r=y.r;
        int mxh=max(v[x.r][0],v[x.r][1]),mx=max(max(x.rm,y.lm),mxh);
        z.ans=x.ans+y.ans+v[x.r][0]+v[x.r][1]-mx;z.am=max(max(x.am,y.am),mxh);z.cnt=x.cnt+y.cnt;
        if(mx==x.rs&&x.cnt==1) z.lm=max(x.am,max(y.lm,mxh)),z.rm=y.rm,z.ls=y.ls,z.rs=y.rs,z.cnt--;
        else if(mx==y.ls&&y.cnt==1) z.lm=x.lm,z.rm=max(y.am,max(mxh,x.rm)),z.ls=x.ls,z.rs=x.rs,z.cnt--;
        else z.lm=x.lm,z.rm=y.rm,z.ls=x.ls,z.rs=y.rs,z.cnt-=(mx==x.rs||mx==y.ls);
        return z;
    }
    void bud(int x,int l,int r)
    {
        if(l==r){s[x].init(l);s[x].l=l;s[x].r=r;return;}
        bud(x*2,l,mid);bud(x*2+1,mid+1,r);s[x]=merge(s[x*2],s[x*2+1]);
    }
    void upd(int x,int l,int r,int p)
    {
        if(l==r){s[x].init(l);return;}
        if(p<=mid) upd(x*2,l,mid,p);else upd(x*2+1,mid+1,r,p);s[x]=merge(s[x*2],s[x*2+1]);
    }
    nod que(int x,int l,int r,int b,int e)
    {
        if(b<=l&&r<=e) return s[x];
        if(e<=mid) return que(x*2,l,mid,b,e);if(b>mid) return que(x*2+1,mid+1,r,b,e);
        return merge(que(x*2,l,mid,b,e),que(x*2+1,mid+1,r,b,e));
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++) scanf("%d",&v[i][0]);
        for(int i=1;i<n;i++) scanf("%d",&v[i][1]);
        for(int i=1;i<=n;i++) scanf("%d",&v[i][2]);
        bud(1,1,n);
        for(int i=1;i<=m;i++)
        {
            scanf("%s",o);
            if(o[0]=='Q') scanf("%d%d",&a,&b),printf("%d
    ",que(1,1,n,a,b).ans);
            else
            {
                scanf("%d%d%d%d%d",&a,&b,&c,&d,&e);if(b>d) swap(b,d);
                if(a!=c) v[b][2]=e,upd(1,1,n,b);else v[b][a-1]=e,upd(1,1,n,b);
            }
        }
    }
    
  • 相关阅读:
    数据库事务之不可重复读
    数据库事务与脏读
    图结构代码实现
    哈希表与散列函数
    数据库表设计与视图
    B树和B+树
    java之字符串中查找字串的常见方法
    剑指 Offer 15. 二进制中1的个数——JS
    剑指 Offer 03. 数组中重复的数字——JS
    算法设计与分析——排序
  • 原文地址:https://www.cnblogs.com/CK6100LGEV2/p/10359722.html
Copyright © 2011-2022 走看看