BZOJ 3995: [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
#分析
我们只需要那线段树来维护一下区间l,r的最小生成树代价 然后再在合并的时候讨论一下即可
#代码

#include <bits/stdc++.h>

#define N 60100
#define INF 0x3f3f3f3f

using namespace std;

int read()
{
    int x = 0;
    int f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

int n , m;
int u[N][2] , d[N][2] , l[N][2] , r[N][2];
int seg[N * 8][5];

void up(int& a , int b)
{
    a = min(a , b);
}

void maintain(int a[5] , int l[5] , int r[5] , int mn , int sm)
{
    memset(a , INF , sizeof(seg[0]));

    up(a[0] , l[0] + r[0] + mn);
    up(a[1] , l[0] + r[1] + mn);
    up(a[0] , l[0] + r[2] + sm);
    up(a[0] , l[0] + r[3] + sm);
    up(a[1] , l[0] + r[3] + mn);
    up(a[1] , l[0] + r[4] + sm);

    up(a[0] , l[1] + r[0] + sm);
    up(a[1] , l[1] + r[1] + sm);
    up(a[1] , l[1] + r[3] + sm);

    up(a[2] , l[2] + r[0] + mn);
    up(a[4] , l[2] + r[1] + mn);
    up(a[2] , l[2] + r[2] + sm);
    up(a[2] , l[2] + r[3] + sm);    
    up(a[4] , l[2] + r[3] + mn);    
    up(a[4] , l[2] + r[4] + sm);    

    up(a[0] , l[3] + r[0] + sm);
    up(a[1] , l[3] + r[1] + sm);
    up(a[4] , l[3] + r[1] + mn);
    up(a[3] , l[3] + r[3] + sm);
    up(a[4] , l[3] + r[3] + mn);
    up(a[4] , l[3] + r[4] + sm);

    up(a[2] , l[4] + r[0] + sm);
    up(a[4] , l[4] + r[1] + sm);
    up(a[4] , l[4] + r[3] + sm);
}

void build(int o , int l , int r)
{
    if(l==r)
    {
        seg[o][0] = d[l][0];
        return;
    }
    else 
    {
        int mid = (l+r)/2 , lson = o*2 , rson = lson+1;
        build(lson , l , mid);
        build(rson , mid+1 , r);

        int mn = min(::r[mid][0] , ::r[mid][1]) , sm = ::r[mid][1]+::r[mid][0];
        maintain(seg[o] , seg[lson] , seg[rson] , mn , sm);
    }
}

void query(int o , int l , int r , int L , int R , int res[5])
{
    if(L <= l && r <= R)   memcpy(res , seg[o] , sizeof(seg[o]));
    else 
    {
        int mid = (l+r)/2 , lson = o*2 , rson = lson+1 , r1[5] , r2[5];
        int mn = min(::r[mid][0] , ::r[mid][1]) , sm = ::r[mid][1]+::r[mid][0];
        if (L <= mid)
        {
            if (R > mid)
            {
                query(lson , l , mid , L , R , r1);
                query(rson , mid+1 , r , L , R , r2);
                maintain(res , r1 , r2 , mn , sm);
            }
            else query(lson , l , mid , L , R , res);
        }
        else query(rson , mid+1 ,r , L , R , res);
    }
}

void modify(int o , int l , int r , int L , int R)
{
    if(L <= l && r <= R)
    {
        if(l==r)
        {
            seg[o][0] = d[l][0];
            return;
        }
        else 
        {
            int mid = (l+r)/2;
            int mn = min(::r[mid][0] , ::r[mid][1]) , sm = ::r[mid][1]+::r[mid][0];
            maintain(seg[o] , seg[o*2] , seg[o*2+1] , mn , sm);
        }
    }
    else 
    {
        int mid = (l+r)/2 , lson = o*2 , rson = o*2+1;
        int mn = min(::r[mid][0] , ::r[mid][1]) , sm = ::r[mid][1]+::r[mid][0];
        if (L <= mid)
            modify(lson , l , mid , L , R);
        if (R >  mid)
            modify(rson , mid+1 , r , L , R);

        maintain(seg[o] , seg[lson] , seg[rson] , mn , sm);
    }
}

int main()
{
    freopen("road.in","r",stdin);
    freopen("road.out","w",stdout);
    cin>>n>>m;
    for(int i=1;i<n;i++)
    {
        int v = read();
        r[i][0] = l[i+1][0] = v;
    }

    for(int i=1;i<n;i++)
    {
        int v = read();
        r[i][1] = l[i+1][1] = v;
    }

    for(int i=1;i<=n;i++)
    {
        int v = read();
        u[i][1] = d[i][0] = v;
    }

    build(1 , 1 , n);

    while(m--)
    {
        int a[5];
        char s[2];
        scanf("%s" , s);
        if(s[0] == 'Q')
        {
            int s = read() , t = read();
            query(1 , 1 , n , s , t , a);
            printf("%d\n" , a[0]);
        }
        else
        {
            int x1 = read() , y1 = read() , x2 = read() , y2 = read() , v = read();
            x1--; x2--;
            if(x1 == x2)
            {
                if (y1 > y2)
                    swap(y1 , y2);
                r[y1][x1] = l[y2][x1] = v;
                modify(1 , 1 , n , y1 , y2);
            }
            else 
            {
                u[y1][1] = d[y1][0] = v;
                modify(1 , 1 , n , y1 , y1);
            }
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值