天梯赛 森森快递

https://pintia.cn/problem-sets/994805046380707840/problems/994805047638999040

 

%%大神O(n)思路双手膜%%

首先嵌套包含的区间只留下最短的那个

 

因为假设区间a包含了区间b

如果最后的答案是区间a贡献1,区间b贡献1

那么因为区间b在区间a里面,

所以如果将这个1的贡献从a给b,对于区间b来说负重不变

而属于a但不属于b的区间就可以避免这部分负重,从而让其他区间产生贡献

 

然后剩余的区间就是要么不相交,要么只相交一部分

将这些区间按左端点从小到大排序

用一个单调队列维护当前范围内区间承重的最小值

每次取队头元素,即为当前允许的最大重量

然后要将区间内的承重减去这个重量

单调队列不支持对已经入队的元素进行修改,这里是减少

但我们可以对未入队的元素进行增加

这样就相当于已入队的元素减少了

用一个变量记录接下来入队的元素要加上多少

 

#include<cstdio>
#include<algorithm>

using namespace std;

#define N 100001

long long a[N];

long long q[N];
int bl[N];

struct node
{
    int l,r;
}e[N],g[N];

bool cmp(node p,node q)
{
    if(p.l!=q.l) return p.l>q.l;
    return p.r<q.r;
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;++i) scanf("%lld",&a[i]);
    for(int i=1;i<=m;++i) 
    {
        scanf("%d%d",&e[i].l,&e[i].r);
        if(e[i].l>e[i].r) swap(e[i].l,e[i].r);
        e[i].l++;
    }
    sort(e+1,e+m+1,cmp);
    int tot=1;
    g[1]=e[1];
    for(int i=2;i<=m;++i)
        if(e[i].r<=g[tot].r) g[++tot]=e[i]; //去除嵌套区间 
    int head=1,tail=1,now=1;
    long long minus=0; //在队里的和未入队的相对差 
    for(int i=tot;i;--i)
    {
        while(now<=g[i].r) 
        {
            while(tail>head && q[tail-1]>a[now]+minus) tail--;
            bl[tail]=now;
            q[tail++]=minus+a[now];
            now++;
        }
        while(bl[head]<g[i].l) ++head;
        minus+=q[head]-minus;
    }    
    printf("%lld",minus);
}

 

posted @ 2020-10-05 15:50  TRTTG  阅读(186)  评论(0编辑  收藏  举报