天梯赛 森森快递
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); }