2016多校10 HDU 5861 Road

11 篇文章 0 订阅
6 篇文章 0 订阅

2016多校联合训练#10

HDU 5861 Road

线段树

传送门:HDU


题意

线性的路上有1到n n个城市,相邻城市间有双向的路,每条路初始时是封闭的。每条路开启时每天有一个维护费用,而且每条路只能被开启以及封闭一次。现在m天里,每天需要从a地运货到b地,你需要保证每天ab之间所有路是开启的。求每天的最小花费。


思路

  1. 因为每条路只能开启一次以及封闭一次,所以需要找到每条路最早使用时间以及最晚使用时间。怎么找?线段树维护区间最大值(序号最大的天,最晚使用)和最小值(同理,最早使用)。把路作为叶子节点,区间更新。

  2. 求每天的最小费用,扫描线思想。一次递归的查询,把每天需要开启和关闭的路记下来,在扫一遍就行了。至于记录的方式,怎么搞都行了,我是用二维的vector,不卡时间。

  3. 最后扫的时候,用一个last变量。每到一天,减去昨天关的路,加上今天开的路,就是今天的花费。


代码

数据范围是200000。。不是100000。。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <iomanip>
#include <string>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

using namespace std;
const int MAXN=200005;
const int oo=1000000007;
typedef long long int LL;
struct Tree{
    int ma;
    int mi;
    int lazy;
}sum[MAXN<<2];
int m,n;
void PushUP(int rt)
{
    sum[rt].ma=max(sum[rt<<1].ma,sum[rt<<1|1].ma);
    sum[rt].mi=min(sum[rt<<1].mi,sum[rt<<1|1].mi);
}
void build(int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt].ma=-1;
        sum[rt].mi=oo;
        sum[rt].lazy=0;
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    PushUP(rt);
}

void PushDown(int rt)
{
    if(sum[rt].lazy)
    {
        sum[rt<<1].lazy=sum[rt<<1|1].lazy=sum[rt].lazy;
        sum[rt<<1].ma=max(sum[rt<<1].ma,sum[rt].ma);
        sum[rt<<1|1].ma=max(sum[rt<<1|1].ma,sum[rt].ma);
        sum[rt<<1].mi=min(sum[rt<<1].mi,sum[rt].mi);
        sum[rt<<1|1].mi=min(sum[rt<<1|1].mi,sum[rt].mi);
        sum[rt].lazy=0;
    }
    return;
}

void update(int L,int R,int p,int l,int r,int rt)
{
    if(L<=l&&r<=R)
    {
        sum[rt].ma=max(sum[rt].ma,p);
        sum[rt].mi=min(sum[rt].mi,p);
        sum[rt].lazy=1;
        return;
    }
    PushDown(rt);
    int m=(l+r)>>1;
    if(L<=m) update(L,R,p,lson);
    if(R>m) update(L,R,p,rson);
}
vector<int> da[MAXN][2];
void query(int l,int r,int rt)
{
    if(l==r)
    {
        if(sum[rt].ma!=-1)
        {
            int d2=sum[rt].ma;
            int d1=sum[rt].mi;
            da[d1][0].push_back(l);
            da[d2][1].push_back(l);
        }
        return;
    }
    PushDown(rt);
    int m=(r+l)>>1;
    query(lson);
    query(rson);
}
int a[MAXN];
int main()
{
    while(scanf("%d%d",&n,&m)==2)
    {
        for(int i=0;i<=m+1;i++)
        {
            da[i][0].clear();
            da[i][1].clear();
        }
        memset(sum,0,sizeof(sum));
        build(1,n-1,1);
        memset(a,0,sizeof(a));
        for(int i=1;i<n;i++)
            scanf("%d",&a[i]);
        for(int i=1;i<=m;i++)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            if(l>r) swap(l,r);
            r--;
            update(l,r,i,1,n-1,1);
        }
        query(1,n-1,1);
        int last=0;
        for(int i=1;i<=m;i++)
        {
            for(int j=0;j<da[i][0].size();j++)
                last+=a[da[i][0][j]];
            printf("%d\n",last);
            for(int j=0;j<da[i][1].size();j++)
                last-=a[da[i][1][j]];
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值