2016 Multi-University Training Contest 10 [HDU 5861] Road (线段树区间更新+差分数组)

链接

HDU 5861


题意

在n个村庄之间存在n-1段路,令某段路开放一天需要交纳wi的费用,但是每段路只能开放一次,一旦关闭将不再开放。现在给你接下来m天内的计划,在第i天,需要对村庄ai到村庄bi的道路进行开放。在满足m天内花费最小的情况下,求出每天的花销。


思路

我们需要求出每段路的开放时间区间[s, t],因为某段路可能在某天不用,但是在这天前后使用了,那么这天同样要收费。
事实上是一个区间更新的问题,我们对道路建立线段树,在第i天给出的信息是道路的区间,我们将这个区间内道路的结束时间全部更新为i,这样我们就能对每段道路得到它时间区间的右端点t。左端点s“先到先得”,在这段道路第一次更新的时候确定其开放的起始时间。
现在每段道路的开放时间区间[s, t]有了,可以将其看做“询问”,对时间建立线段树,每段道路令[s, t]内的每天增加wi的费用,这就又是一个区间更新的问题。
但是这次的更新比较简单,只是单纯让区间内的所有值增上某个值而已,使用差分数组就可以在O(n)内解决了。
PS:这道题目感觉出得还挺好的,如果对线段树的lazy标记理解不深刻就可能容易做不出来。因为在成段更新区间的时候,对开始时间和结束时间的处理是完全不同的。

代码
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long lint;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 200020;
int Begin[maxn << 2], End[maxn << 2];
void build(int l, int r, int rt)
{
    Begin[rt] = End[rt] = 0;
    if(l == r) return ;
    int m = (l + r) >> 1;
    build(lson), build(rson);
}

inline void _push_down(int rt)
{
    if(!Begin[rt<<1]) Begin[rt<<1] = Begin[rt];
    if(!Begin[rt<<1|1]) Begin[rt<<1|1] = Begin[rt];

    if(!End[rt]) return ;
    End[rt<<1] = End[rt<<1|1] = End[rt];
    End[rt] = 0;
}

void update(int L, int R, int k, int l, int r, int rt)
{
    if(L <= l && r <= R)
    {
        if(!Begin[rt]) Begin[rt] = k;
        End[rt] = k;
        return ;
    }
    _push_down(rt);

    int m = (l + r) >> 1;
    if(m >= L) update(L, R, k, lson);
    if(m < R) update(L, R, k, rson);
}

int bg[maxn], ed[maxn];
void push_all(int l, int r, int rt)
{
    if(l == r)
    {
        bg[l] = Begin[rt], ed[l] = End[rt];
        return ;
    }
    _push_down(rt);
    int m = (l + r) >> 1;
    push_all(lson), push_all(rson);
}

void test(int l, int r)
{
    for(int i = l; i <= r; i++)
    {
        printf("# bg = %d, ed = %d\n", bg[i], ed[i]);
    }
}

int w[maxn];
lint a[maxn], d[maxn];
int main()
{
    //freopen("5.txt", "r", stdin);

    int n, m;
    while(cin >> n >> m)
    {
        build(1, n-1, 1);
        for(int i = 1; i < n; i++)
            scanf("%d", &w[i]);
        for(int i = 1, p, q; i <= m; i++)
        {
            scanf("%d%d", &p, &q);
            if(p > q) swap(p, q);
            update(p, q-1, i, 1, n-1, 1);
        }
        push_all(1, n-1, 1);

        //test(1, n-1);

        memset(d, 0, sizeof(lint)*(m+10));
        for(int i = 1; i < n; i++) if(bg[i])
        {
            d[bg[i]] += w[i];
            d[ed[i]+1] -= w[i];
        }
        a[0] = 0;
        for(int i = 1; i <= m; i++)
        {
            a[i] = a[i-1] + d[i];
            printf("%I64d\n", a[i]);
        }
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值