线段树+扫描线

扫描线求面积

模型

有若干个矩形,给出每个矩形的左下和右上坐标,问这些矩形的面积交。

就是阴影部分的面积

算法流程

用扫描线,我们按每个矩形分成为两条扫描线,上扫描线(-)和下扫描线(+)。

struct Line {
    double l, r, h;//覆盖的x轴起点和终点,高度h
    int k;//上/下边
}a[10005];

那么面积就是|这条扫描线的高度-上条扫描线的高度|*(扫描线覆盖的x轴区间大小)
ans+=(a[i].ha[i1].h)(线x)
扫描线覆盖的x轴区间大小:用线段树维护,下边在线段树上加,上边在线段树上减。
把扫描线排序后,开始处理,h相同先处理+
加入第一条扫描线前:
ans+=(a[1].ha[0].h)(0)//ans=0

加入第二条扫描线前:
扫描线覆盖的x轴区间大小=2
ans+=(a[2].ha[1].h)(2),a[2].h==a[1].h//ans=0

加入第4条扫描线前,扫描线覆盖的x轴区间大小=4。
ans+=(a[1].ha[0].h)(4)//ans+=14=4

实际上这次求的是这部分面积:

加入第4条扫描线前,扫描线覆盖的x轴区间大小=5。
ans+=(a[1].ha[0].h)(5)//ans+=15=9

这次求的面积:

加入第5条扫描线前,扫描线覆盖的x轴区间大小=4。
ans+=(a[1].ha[0].h)(4)//ans+=24=17

这次求的面积:

加入第6条扫描线前,扫描线覆盖的x轴区间大小=2。

这次求的面积:

所有的面积都不重不漏的计算出来了。

具体实现

把x坐标离散化。用lza标记这个区间是否全部覆盖。

if(lza[i])sum[i]=这个区间的长度
else{
      if(i是叶子节点){
            sum[i]=0
      }      
      else{
            sum[i]=sum[i<<1]+sum[i<<1|1]
      }
}

这样就可以维护加线段和减线段了

#include  <map>
#include  <set>
#include  <cmath>
#include  <queue>
#include  <cstdio>
#include  <vector>
#include  <climits>
#include  <cstring>
#include  <cstdlib>
#include  <iostream>
#include  <algorithm>
#define LL long long
using namespace std;

double z[205];
struct Tree {
    int laz[2000005];
    double sum[2000005];
    void BT(int i, int l, int r) {
        sum[i]=laz[i]=0;
        if(l==r)
            return ;
        int mid=l+r>>1;
        BT(i<<1, l, mid), BT(i<<1|1, mid+1, r);
    }

    void psuh_up(int i, int l, int r) {
        if(laz[i]) { //全部覆盖
            sum[i]=z[r]-z[l-1];
        } else if(l==r) { //叶子节点
            sum[i]=0;
        } else {
            sum[i]=sum[i<<1]+sum[i<<1|1];
        }

    }
    void up_data(int i, int l, int r, int L, int R, int val) {
        if(R<L)
            return ;
        if(l==L&&r==R) {
            laz[i]+=val;
            psuh_up(i, l, r);
            return ;
        }
        int mid=l+r>>1;
        if(R<=mid)
            up_data(i<<1, l, mid, L, R, val);
        else if(L>mid)
            up_data(i<<1|1, mid+1, r, L, R, val);
        else
            up_data(i<<1, l, mid, L, mid, val), up_data(i<<1|1, mid+1, r, mid+1, R, val);
        psuh_up(i, l, r);
    }
} T;

struct Line {
    double l, r, h;
    int k;//上/下边
}a[105];

int cmp1(double a, double b){return a<b;}
int cmp2(const Line &a, const Line &b){return a.h==b.h?a.k>b.k:a.h<b.h;}

int main() {

    int n, cas=1;
    double x, y, x2, y2;
    while(scanf("%d", &n), n) {
        for(int i=1; i<=n; i++) {
            scanf("%lf%lf%lf%lf", &x, &y, &x2, &y2);
            a[i*2-1]={x, x2, y, 1};
            a[i*2]={x, x2, y2, -1};
            z[i*2-1]=x, z[i*2]=x2;
        }

        //离散化x坐标
        sort(z+1, z+2*n+1, cmp1);
        int cnt=unique(z+1, z+2*n+1)-z-1;
        T.BT(1, 1, cnt);
        for(int i=1; i<=2*n; i++){
            a[i].l=lower_bound(z+1, z+cnt+1, a[i].l)-z;
            a[i].r=lower_bound(z+1, z+cnt+1, a[i].r)-z;
        }

        double ans=0;
        sort(a+1, a+2*n+1, cmp2);
        for(int i=1; i<=2*n; i++){
            double H=a[i].h-a[i-1].h;
            ans+=H*T.sum[1];
            T.up_data(1, 1, cnt, a[i].l+1, a[i].r, a[i].k);
        }
        printf("Test case #%d\n", cas++);
        printf("Total explored area: %.2f\n\n", ans);
    }

    return 0;
}
/*
2
10 10 20 20
15 15 25 25.5
0

Test case #1
Total explored area: 180.00 
*/

扫描线求周长

有若干个矩形,给出每个矩形的左下和右上坐标,问这些矩形相交的外周长。

给个结论吧:
求横着的边,把每一条线段加入线段树。ans+=|现在的区间覆盖长度-上次的区间覆盖长度|
求竖着的边,同样的道理用线段树处理一下就可以了。
上面已经有维护覆盖的线段树,直接用就可以了。

#include  <bits/stdc++.h>
#define LL long long
using namespace std;

double z[50005];
struct Tree {
    int laz[2000005];
    double sum[2000005];
    void BT(int i, int l, int r) {
        sum[i]=laz[i]=0;
        if(l==r)
            return ;
        int mid=l+r>>1;
        BT(i<<1, l, mid), BT(i<<1|1, mid+1, r);
    }

    void psuh_up(int i, int l, int r) {
        if(laz[i]) { //全部覆盖
            sum[i]=z[r]-z[l-1];
        } else if(l==r) { //叶子节点
            sum[i]=0;
        } else {
            sum[i]=sum[i<<1]+sum[i<<1|1];
        }

    }
    void up_data(int i, int l, int r, int L, int R, int val) {
        if(R<L)
            return ;
        if(l==L&&r==R) {
            laz[i]+=val;
            psuh_up(i, l, r);
            return ;
        }
        int mid=l+r>>1;
        if(R<=mid)
            up_data(i<<1, l, mid, L, R, val);
        else if(L>mid)
            up_data(i<<1|1, mid+1, r, L, R, val);
        else
            up_data(i<<1, l, mid, L, mid, val), up_data(i<<1|1, mid+1, r, mid+1, R, val);
        psuh_up(i, l, r);
    }
} Tx, Ty;

struct Line {
    double l, r, h;
    int k;//上/下边
}a[10005], b[10005];

int cmp1(double a, double b){return a<b;}
int cmp2(const Line &a, const Line &b){return a.h==b.h?a.k>b.k:a.h<b.h;}

int main() {

    int n;
    double x, y, x2, y2;
    while(~scanf("%d", &n)) {
        if(n==0){
            printf("0\n");
            continue;
        }
        int m=0;
        for(int i=1; i<=n; i++) {
            scanf("%lf%lf%lf%lf", &x, &y, &x2, &y2);
            a[i*2-1]={x, x2, y, 1};
            a[i*2]={x, x2, y2, -1};
            b[i*2-1]={y, y2, x, 1};
            b[i*2]={y, y2, x2, -1};

            z[++m]=x, z[++m]=x2;
            z[++m]=y, z[++m]=y2;
        }

        //离散化x和y坐标
        sort(z+1, z+m+1, cmp1);
        int cnt=unique(z+1, z+m+1)-z-1;

        Tx.BT(1, 1, cnt); Ty.BT(1, 1, cnt);

        for(int i=1; i<=2*n; i++){
            a[i].l=lower_bound(z+1, z+cnt+1, a[i].l)-z;
            a[i].r=lower_bound(z+1, z+cnt+1, a[i].r)-z;
            b[i].l=lower_bound(z+1, z+cnt+1, b[i].l)-z;
            b[i].r=lower_bound(z+1, z+cnt+1, b[i].r)-z;
        }

        double ans=0;
        sort(a+1, a+2*n+1, cmp2);
        sort(b+1, b+2*n+1, cmp2);
        double lastx=0, lasty=0;
        for(int i=1; i<=2*n; i++){
            //处理x坐标
            Tx.up_data(1, 1, cnt, a[i].l+1, a[i].r, a[i].k);
            ans+=fabs(Tx.sum[1]-lastx);
            lastx=Tx.sum[1];

            //处理y坐标
            Ty.up_data(1, 1, cnt, b[i].l+1, b[i].r, b[i].k);
            ans+=fabs(Ty.sum[1]-lasty);
            lasty=Ty.sum[1];
        }
        printf("%.0f\n", ans);
    }

    return 0;
}
/*
7
-15 0 5 10
-5 8 20 25
15 -4 24 14
0 -6 16 4
2 15 10 22
30 10 36 20
34 0 40 16

228
*/

至少被覆盖2次的面积

题目

覆盖的面积(http://acm.hdu.edu.cn/showproblem.php?pid=1255)

题目背景

给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.

输入

输入数据的第一行是一个正整数T(1<=T<=100),代表测试数据的数量.每个测试数据的第一行是一个正整数N(1<=N<=1000),代表矩形的数量,然后是N行数据,每一行包含四个浮点数,代表平面上的一个矩形的左上角坐标和右下角坐标,矩形的上下边和X轴平行,左右边和Y轴平行.坐标的范围从0到100000.
注意:本题的输入数据较多,推荐使用scanf读入数据.

输出

对于每组测试数据,请计算出被这些矩形覆盖过至少两次的区域的面积.结果保留两位小数.

样例输入

2
5
1 1 4 2
1 3 3 7
2 1.5 5 4.5
3.5 1.25 7.5 4
6 3 10 7
3
0 0 1 1
1 0 2 1
2 0 3 1

样例输出

7.63
0.00

思路

170ms laz标记
题意:求N个矩形中,求被覆盖至少俩次的面积和

分析:覆盖两次即col[rt]>=2就好。一开始将线段pushdown到叶子节点,根据col[rt]>=2才pushup上来,差点超时了,其实可以lazy标志,整段更新的,只是没想到而已。

用sum[rt][0]表示该节点rt代表的线段被覆盖一次的长度之和,则

if(col[rt])sum[rt][0]=pos[r+1]-pos[l];$//整段被覆盖,全加上
else if(l==r)sum[rt][0]=0;//叶子节点没有子孙节点的覆盖传递上来,所以清零
else sum[rt][0]=sum[rt<<1][0]+sum[rt<<1|1][0];//加上子孙节点被覆盖着的长度

同理sum[rt][1]表示该节点rt代表的线段被覆盖两次的长度之和。则

if(col[rt]>1)sum[rt][1]=sum[rt][0];//整段被覆盖两次以上,全加上
else if(l==r)sum[rt][1]=0;//叶子节点没有子孙节点的覆盖两次的长度传递上来,故清零
else if(col[rt]==1)sum[rt][1]=sum[rt<<1][0]+sum[rt<<1|1][0];//加上之前子孙节点上被覆盖一次的长度,刚好共两次(覆盖一次的长度一定大于覆盖一次的)
else sum[rt][1]=sum[rt<<1][1]+sum[rt<<1|1][1];//加上子孙节点覆盖两次的总长度

如果题目求覆盖3次,4次都可以以此类推pushup上来。

#include <bits/stdc++.h>
#define LL long long
#define N 2015
using namespace std;
double pos[2 * N];
struct seg {
    double l,r,h;
    int v;
    seg() {}
    seg(double l,double r,double h,int v):l(l),r(r),h(h),v(v) {}
    bool operator <(const seg a)const {
        return h<a.h;
    }
} s[2*N];
int n,col[N<<2];
double sum[N<<2][3];

int bin(double key,int low,int high) {
    while(low <= high) {
        int mid=(low+high)>>1;
        if(pos[mid] == key) return mid;
        else if(pos[mid] < key) low=mid+1;
        else high=mid-1;
    }
    return -1;
}

void Pushup(int l,int r,int rt) {
    if(col[rt]) sum[rt][0]=pos[r+1]-pos[l];
    else if(l==r) sum[rt][0]=0;
    else sum[rt][0]=sum[rt<<1][0]+sum[rt<<1|1][0];
    if(col[rt]>1) sum[rt][1]=sum[rt][0];
    else if(l==r) sum[rt][1]=0;
    else if(col[rt]==1) sum[rt][1]=sum[rt<<1][0]+sum[rt<<1|1][0];
    else sum[rt][1]=sum[rt<<1][1]+sum[rt<<1|1][1];
}
void update(int L,int R,int c,int l,int r,int rt) {
    if(L<=l&&r<=R) {
        col[rt]+=c;
        Pushup(l,r,rt);
        return;
    }
    int m=(l+r)>>1;
    if(L<=m) update(L,R,c,l,m,rt<<1);
    if(m<R) update(L,R,c,m+1,r,rt<<1|1);
    Pushup(l,r,rt);
}

int main() {
    int T;
    scanf("%d",&T);
    while(T--) {
        scanf("%d",&n);
        int i,k;
        for(i=0,k=0; i<n; i++) {
            double x1,y1,x2,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            pos[k]=x1;
            s[k++]=seg(x1,x2,y1,1);
            pos[k]=x2;
            s[k++]=seg(x1,x2,y2,-1);
        }
        sort(pos,pos+k);
        sort(s,s+k);
        int m=1;
        for(i=1; i<k; i++){
            if(pos[i]!=pos[i-1]){
                pos[m++]=pos[i];
            }
        }
    
        double res=0;
        for(i=0; i<k; i++) {
            int l=bin(s[i].l,0,m-1);//离散化
            int r=bin(s[i].r,0,m-1)-1;//离散化
            
            update(l,r,s[i].v,0,m-1,1);
            res += sum[1][1]*(s[i+1].h - s[i].h);
        }
        printf("%.2lf\n",res);
    }
    return 0;
}

矩阵覆盖最大价值

题目

P1502 窗口的星星(https://www.luogu.com.cn/problem/P1502)

题目背景

小卡买到了一套新房子,他十分的高兴,在房间里转来转去。

题目描述

晚上,小卡从阳台望出去,“哇~~~~好多星星啊”,但他还没给其他房间设一个窗户。

天真的小卡总是希望能够在晚上能看到最多最亮的星星,但是窗子的大小是固定的,边也必须和地面平行。

这时小卡使用了超能力(透视术)知道了墙后面每个星星的位置和亮度,但是小卡发动超能力后就很疲劳,只好拜托你告诉他最多能够有总和多亮的星星能出现在窗口上。

输入格式

本题有多组数据,第一行为 TT,表示有 TT 组数据。

对于每组数据:

第一行 3 个整数 n,W,H表示有 n 颗星星,窗口宽为 W,高为 H。

接下来 n 行,每行三个整数 xi,yi,li表示星星的坐标在 (xi,yi),亮度为 li

输出格式

TT 个整数,表示每组数据中窗口星星亮度总和的最大值。

输入

2
3 5 4
1 2 3
2 3 2
6 3 1
3 5 4
1 2 3
2 3 2
5 3 1

输出

5
6
说明/提示
小卡买的窗户框是金属做的,所以在边框上的不算在内。

数据范围

1≤T≤10
1≤n≤1e4
1≤W,H≤1e6
0≤xi, yi<2^31

思路

我们把问题转化一下:将窗口的左下角位于一颗星星的地方一定是最优的。那么我们可以定义一颗星星的管辖范围为一个以它为左下角的,长为 W,宽为 H 的矩形。这样就拥有了一个很好的性质:如果两个矩形有交点,就说明这两个节点可以同时出现在窗口中。这样我们可以使用一条扫描线从左往右扫过整个平面,让线段树中存储的矩形均为左竖线与扫描线距离在 W 之内的矩形。接下来这其中的矩形均为可以水平相交的矩形,接下来只要用线段树解决竖直方向相交的问题了。我们在线段树上定义一个节点为可以覆盖到这个节点的所有矩形的权值之和,答案就是所有节点中的最大值。

#include  <bits/stdc++.h>
#define LL long long
#define mid ((l+r)/2)
using namespace std;

struct Line{
    int l, r, x, val;
}a[20005];

struct SegTree {
    LL mx[20005*4+10], laz[20005*4+10];
    void BT(int i, int l, int r) {
        mx[i]=laz[i]=0;
        if(l==r) {
            mx[i]=0;
            return ;
        }
        BT(i<<1, l, mid);
        BT((i<<1)+1, mid+1, r);
        mx[i]=max(mx[i<<1], mx[(i<<1)+1]);
    }

    void add(int i, int l, int r, int L, int R, int v) {

        if(l==L&&r==R) {
            laz[i]+=v;
            mx[i]+=v;
            return ;
        }
        if(R<=mid)
            add(i<<1, l, mid, L, R, v);
        else if(L>mid)
            add((i<<1)+1, mid+1, r, L, R, v);
        else
            add(i<<1, l, mid, L, mid, v), add((i<<1)+1, mid+1, r, mid+1, R, v);
        mx[i]=max(mx[i<<1], mx[(i<<1)+1])+laz[i];
    }

} T;


int z[20005];
int main() {

    int t; scanf("%d", &t);
    while(t--){
        int n, W, H; scanf("%d%d%d", &n, &W, &H);
        int m=0;
        for(int i=1; i<=n; i++){
            int x, y, val; scanf("%d%d%d", &x, &y, &val); 
            a[i]={y, y+H-1, x, val};
            z[++m]=y;
            z[++m]=y+H-1;
        }
        //离散化
        sort(z+1, z+m+1);
        int cut=unique(z+1, z+m+1)-z-1;
        T.BT(1, 1, cut);
        for(int i=1; i<=n; i++){
            a[i].l=lower_bound(z+1, z+cut+1, a[i].l)-z;
            a[i].r=lower_bound(z+1, z+cut+1, a[i].r)-z;
        }

        sort(a+1, a+n+1, [](Line &a, Line &b){return a.x<b.x;});
        int pos=1;
        LL ans=0;
        for(int i=1; i<=n; i++){
            while(a[pos].x-a[i].x<W&&pos<=n){
                T.add(1, 1, cut, a[pos].l, a[pos].r, a[pos].val);
                pos++;
            }
            ans=max(ans, T.mx[1]);
            T.add(1, 1, cut, a[i].l, a[i].r, -a[i].val);
        }
        printf("%lld\n", ans);
    }

    return 0;
}

矩阵每行每列完全覆盖

1559 车和矩形(http://www.51nod.com/Challenge/Problem.html#problemId=1559)

题目描述

波雷卡普有一个n×m,大小的棋盘,上面有k个车。他又放了q个矩形在上面。每一个矩形要受到保护。矩形受到保护的意思是对于该矩形内部所有的格子能够被这个矩形内的某个车攻击到或者被占据,和矩形外面的车无关,即矩形外面的车不能攻击到矩形里面。车的位置是固定的。

样例解释:

对于最后一个矩形,用红色框框表示的,因为(1,2)不能被某个车攻击到,所以是NO。

输入

单组测试数据。
第一行有4个整数 n, m, k 和q (1≤n,m≤100000, 1≤k,q≤200000),表示棋盘大小,棋盘上车的数目,放置矩形的数目。
棋盘的列是从左到右按照1到n编号,行是从下到上按照1到m编号。
接下来k行每一行有两个整数x y(1≤x≤n,1≤y≤m),表示车的位置。输入保证所有车的位置是不一样的。
接下来q行每一行有四个整数x1 y1 x2 y2(1≤x1≤x2≤n, 1≤y1≤y2≤m)。
表示符合x1≤x≤x2, y1≤y≤y2的格子在该矩形内。

输出

对于每一个矩形,如果他是受保护的输出YES,否则输出NO。

样例输入

4 3 3 3
1 1
3 2
2 3
2 3 2 3
2 1 3 3
1 2 2 3

样例输出

YES
YES
NO

思路

我先说自己的两个思路:
第一个主席树:我们如果对横坐标和纵坐标分别一次处理。把x坐标当作线段的叶子节点。然后按y的大小从小到大加入权值线段树[y, y]++。
维护最小值。那么对于一个询问[y, y2]查询root=y2的[x, x2]-root=(y2-1)的[x, x2]就是区间[x, x2]的覆盖次数,如果>0就说明完全被覆盖。
然而是错的,样例红框。维护是信息错误的。
第二个:莫队+线段树:T了。
第三个:一个矩形要想被保护的话,必须满足所有行 / 所有列上均有一个车的限制。这样我们解决问题就可以转化为判断一个矩形所有行上是否都有一个车了(对于列只需要把图转一下做就可以)。这样我们自然想到线段树 + 扫描线,我是用列上的扫描线从上到下,每一次扫到一个矩形的下边界就判断一下。线段树上维护每一列上距离最远的一个车的纵坐标,我们判断一个矩形是否被保护就只需要判断最远的车是否在当前矩形的范围内了。感觉OFN说的扫描线就是把问题转化到一个前缀的维护上面去其实是挺有道理的恩~

#pragma GCC optimize(3, "Ofast", "inline")
#include<bits/stdc++.h>
#define LL long long
using namespace std;

char buf[1<<20],*p1=buf,*p2=buf;
inline int read(){
    char c=getchar();LL x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

const int N=1e5+10;
struct SegTree {
    LL T[N<<2];
    inline void pushup(int o) { T[o]=min(T[o<<1],T[o<<1|1]); }
    void add(int o,int l,int r,int pos,LL v){//修改 T[o]=v
        if(l==r) { T[o]=v; return; }
        int mid=l+r>>1;
        pos<=mid ? add(o<<1,l,mid,pos,v) : add(o<<1|1,mid+1,r,pos,v);
        pushup(o);
    }
    LL query(int o,int l,int r,int ql,int qr){//查询区间最大值

        if(l>qr||r<ql) return 1<<30;  //查询最小值修改
        if(l>=ql&&r<=qr) return T[o];
        int mid=l+r>>1; return min(query(o<<1,l,mid,ql,qr),query(o<<1|1,mid+1,r,ql,qr));
    }

}Tx, Ty;

struct Node {
    int x, y;
};
vector<Node> a[100005], b[100005];
struct Qry{
    int x, y, x2, y2, id;
};
vector<Qry> qx[100005], qy[100005];

int ans[200005];
int main() {
    int n=read(), m=read(), k=read(), Q=read();
    for(int i=1; i<=k; i++) {
        int x=read(), y=read();
        a[y].push_back({x, y});
        b[x].push_back({x, y});
    }
    for(int i=1; i<=Q; i++){
        int x=read(), y=read(), x2=read(), y2=read();
        qx[y2].push_back({x, y, x2, y2, i});
        qy[x2].push_back({x, y, x2, y2, i});
    }
    for(int i=1; i<=100000; i++){
        for(auto to: a[i]){
            Tx.add(1, 1, 100000, to.x, i);
        }
        for(auto to: qx[i]){
            ans[to.id]|=Tx.query(1, 1, 100000, to.x, to.x2)>=to.y;
        }

        for(auto to: b[i]){
            Ty.add(1, 1, 100000, to.y, i);
        }
        for(auto to: qy[i]){
            ans[to.id]|=Ty.query(1, 1, 100000, to.y, to.y2)>=to.x;
        }
    }
    for(int i=1; i<=Q; i++){
        printf("%s\n", ans[i]?"YES":"NO");
    }
}

线段树维护不定区间的最值问题

题目


思路:这个做法我上次在计蒜之道上遇见过一个用线段树维护DP的,不过我不知道这个也算是扫描线。
我们用一棵线段树,每个叶子节点x代表以x为区间左端点时的最大值。那么我们每次加一个数。
假如加入的是a[y]那么就是以y为右端点。我们考虑以y为右端点时,假设a[y]的颜色是id,以[1, pre[id]]为左端点时。
id颜色已经出现了,那么[1, pre[id]]+a[y],而[pre[id]+1, y]没有出现过+a[y]+k。
附计蒜之道这题我的博客:https://blog.nowcoder.net/n/a7f34ea7ecef4a02af9e72f85fb83d2b

#include<bits/stdc++.h>
#define LL long long
#define mid ((l+r)/2)
using namespace std;

struct SegTree {
    int mx[100005*4+10], laz[100005*4+10];
    void BT(int i, int l, int r) {
        mx[i]=laz[i]=0;
        if(l==r) {
            mx[i]=0;
            return ;
        }
        BT(i<<1, l, mid);
        BT((i<<1)+1, mid+1, r);
        mx[i]=max(mx[i<<1], mx[(i<<1)+1]);
    }

    void add(int i, int l, int r, int L, int R, int v) {

        if(l==L&&r==R) {
            laz[i]+=v;
            mx[i]+=v;
            return ;
        }
        if(R<=mid)
            add(i<<1, l, mid, L, R, v);
        else if(L>mid)
            add((i<<1)+1, mid+1, r, L, R, v);
        else
            add(i<<1, l, mid, L, mid, v), add((i<<1)+1, mid+1, r, mid+1, R, v);
        mx[i]=max(mx[i<<1], mx[(i<<1)+1])+laz[i];
    }
} T;

int a[100005], b[100005], pre[100005];
int main() {
    int n, k; scanf("%d%d", &n, &k);
    int ans=-1<<30;
    for(int i=1; i<=n; i++){
        int x, id; scanf("%d%d",&x, &id);
        if(pre[id]) T.add(1, 1, n, 1, pre[id], x);
        T.add(1, 1, n, pre[id]+1, i, x+k);
        ans=max(ans, T.mx[1]);
    }
    printf("%d\n", ans);

    return 0;
}

posted @   liweihang  阅读(300)  评论(1编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
Live2D
欢迎阅读『线段树+扫描线』
点击右上角即可分享
微信分享提示