2016 Multi-University Training Contest 10 [HDU 5861] Road (线段树:区间覆盖+单点最大小)

HDU 5861

题意

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

分析:

我们可以想到用线段树想到记录每一段路的开始时间与结束时间,开始时间很简单,就是一开始的时间,结束的时间求法可以参考区间覆盖,这是类似的;

然后我们在转化哪一天开哪些,哪一天关哪些,那这天的贡献sum = 开-关 ;

这很关键,我在比赛就没有想出来。。

例:如st[1]=3,表示第1段道路的最早开始时间是第3天,那么你可以start[3].push_back(1),表示第3天开启第1段道路,这样扫一遍过去就行了;

这是一种,要不就用d[be[i]]+=w[i] , d[en[i]]-=w[i];  其实差不多

#include<bits/stdc++.h>

using namespace std ;
#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];
int be[maxn],en[maxn],w[maxn];
long long sum[maxn],d[maxn];
void pushdown(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 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);
}

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 ;
    }
    pushdown(rt);
    int m=(l+r) >> 1;
    if(m>=L)
    update(L,R,k,lson);
    if(m<R)
    update(L,R,k,rson);
}
void pushall(int l , int r , int rt)
{
    if(l==r)
    {
        be[l]=Begin[rt],en[l]=End[rt];
        return ;
    }
    pushdown(rt);
    int m=(l+r)>>1;
    pushall(lson);
    pushall(rson);
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        n--;
        build(1,n,1);///建树
        for(int i=1 ; i<=n ; i++)
        scanf("%d",&w[i]);

        for(int i=1 ; i<=m ; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            if(u>v)//防止意外
            swap(u,v);
            update(u,v-1,i,1,n,1);///u到v区间更新为i(天);
        }
        pushall(1,n,1);//找到每一段路的开始时间与结束时间
        memset(d,0,sizeof(d));
        for(int i=1 ; i<=n ; i++)//每一段路的开始费用与结束费用
        {
            if(be[i])
            {
                d[be[i]]+=w[i];
                d[en[i]+1]-=w[i];
            }
        }
        sum[0]=0;
        for(int i=1 ; i<=m ; i++)///类似与扫描线,一天一天的扫过去
        {
            sum[i]=sum[i-1]+d[i];
            printf("%lld\n",sum[i]);
        }
    }
}

线段树真厉害,以后就不要只是固定与模板,要与线段树的结构与自己需要用的功能结合

原文地址:https://www.cnblogs.com/shuaihui520/p/9795013.html

时间: 2024-11-06 03:43:01

2016 Multi-University Training Contest 10 [HDU 5861] Road (线段树:区间覆盖+单点最大小)的相关文章

HDU 4509 湫湫系列故事——减肥记II(线段树-区间覆盖 或者 暴力技巧)

http://acm.hdu.edu.cn/showproblem.php?pid=4509 题目大意: 中文意义,应该能懂. 解题思路: 因为题目给的时间是一天24小时,而且还有分钟.为了解题方便,我们将小时换成分钟,那么一天24小时,总共有1440分钟.顾我就可以把一天里的任意HH:MM时间换成分钟.就这样一天的时间就变成[0,1440]区间了. 因为所给的活动最多是5*10^5,如果把活动的时间在线段[0,1440]都修改,那么时间的复杂度最坏是O(5*10^5*1440). (1)方法一

2015 Multi-University Training Contest 10 hdu 5406 CRB and Apple

CRB and Apple Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 421    Accepted Submission(s): 131 Problem Description In Codeland there are many apple trees.One day CRB and his girlfriend decide

HDU 5861 Road(线段树 区间修改 单点查询)

Road Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1132    Accepted Submission(s): 309 Problem Description There are n villages along a high way, and divided the high way into n-1 segments. E

HDU 3308 LCIS (线段树区间合并)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308 题目很好懂,就是单点更新,然后求区间的最长上升子序列. 线段树区间合并问题,注意合并的条件是a[mid + 1] > a[mid],写的细心点就好了. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int MAXN = 1

hdu 1116 敌兵布阵 线段树 区间求和 单点更新

线段树的基本知识可以先google一下,不是很难理解 线段树功能:update:单点增减 query:区间求和 #include <bits/stdc++.h> #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 using namespace std; const int MAXN = 50008; int sum[MAXN<<2]; void build(int l, int r, int rt)

HDU 5316 Magician(线段树区间合并, 子序列最值 多校2015啊)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5316 Problem Description Fantasy magicians usually gain their ability through one of three usual methods: possessing it as an innate talent, gaining it through study and practice, or receiving it from an

HDU 3308 LCIS 线段树 区间更新

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3308 题目描述: 有两种操作, U x y  , 第xth赋值为y .Q x y , 查询区间x-y的最长连续上升子序列的长度L 解题思路: 对于线段树不好的我依然好难.....有太多细节需要注意了....但是这是一道很好的题, 一段区间的L可能从三个地方来, 一种是中间, 一种是以左起点为开头的, 一种是以右起点结尾的, 这样查询的时候就要注意了: 如果两段的中间值是a[m] < a[m+1]

[HDU] 2795 Billboard [线段树区间求最值]

Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 11861    Accepted Submission(s): 5223 Problem Description At the entrance to the university, there is a huge rectangular billboard of s

HDU 1540(线段树+区间合并)学习记录

学习了线段树的新姿势,记录一下 参考blog:https://blog.csdn.net/sunyutian1998/article/details/79618316 query的时候m-ql+1和qr-m写成了m-l+1.r-m,wa了几发之后才找到bug 错误样例: 10 1Q 5 wrong answer:5 顺带一提,这个题目可以在set上面二分直接过,不过最主要还是为了练习区间合并 #include<bits/stdc++.h> using namespace std; const