CDQ 分治乱学

前言

  • 今天学 \(CDQ\) 分治呢

简介

  • \(CDQ\) 分治,陈丹琦提出的,e,大手子%%%
  • 小蓝书上说这是基于时间的分治,后面说(· w ·)
  • 颜文字是和 9G 学长学的
  • 其实 \(CDQ\) 分治主要是解决偏序问题,是离线算法,本质和分治区别不大,没有固定的算法,复杂度大概是 \(O(n\) \(\log^k\) \(n)\) 的,其中 \(k\) 是偏序维度 \(-1\)

偏序

  • 什么是偏序捏?
  • 定义一个 \(R\) 为集合 \(x\) 和集合 \(y\) 的一个二元关系,有以下性质的即为偏序关系:
    • 自反性:对\(\forall\) \(x∈A\),有 \(xRx\)
    • 反对称性:对\(\forall\) \(x∈A\) , \(y∈A\),有 \((xRy \land xRy) \to a=b\)
    • 传递性:对\(\forall\) \(x∈A\) , \(y∈A\) , \(z∈A\),有 \((xRy \land yRz) \to xRz\)
  • 看起来很吊,但其实 \(\le\) 就是偏序关系
  • 理解了偏序后,\(CDQ\) 分治就很好理解了

\(CDQ\) 分治

  • \(CDQ\) 分治本质上就是分治,上面也提到了,一共分三个操作:
    • 二分区间,向下递归
    • 双指针扫扫扫
    • 统计左区间对右区间的贡献
  • 顺便排一下序
  • 我们结合例题看

例题P1908 逆序对

  • 我们前两天刚说了二维数点,现在来说 \(CDQ\) 分治
  • 这个问题本质上是一个二维偏序:
    • \(a_i > a_j\)
    • \(i < j\)
  • 其实并非定义上的偏序
  • 我们发现第二维:\(i < j\) 是排好的,所以我们仅需搞第一维
  • 归并排序可以认为是 \(CDQ\) 分治
  • 代码不给了,随便找个题解看一下吧,咕咕咕
  • 我们再来一道

例题P4390 [BalkanOI 2007] Mokia 摩基亚

  • 给个简明题干
题干
  • 维护一个 \(w\times w\) 的矩阵,初始值均为 \(s\)。每次操作可以增加某格子的权值,或询问某子矩阵的总权值。修改操作数 \(m\),询问数 \(q\)
  • 我们观察发现直接统计矩形不好算,所以我们用二维前缀的思想搞一下,就可以方便的计算
  • 怎么算呢
  • 其实这个问题是一个三维偏序,只不过有一维已经处理好了,就是时间这一维
  • 三维分别是:\(x\)\(y\),时间
  • 时间默认排好序,所以不管
  • 我们如何处理这两维捏?
  • 想想逆序对的做法,类似的,我们用 \(CDQ\) 干掉一维,再用 \(DS\) 干掉一维,就解决了,sbmqwm你想干嘛
啸剧场
  • sbmqwm:你猜
  • msjing:?
  • sbmqwm:我来模拟一下如何干掉一维
  • msjing:何一维

批注 2026-07-21 163602

  • \(from\) 百度百科)
  • sbmqwm:你
  • 以后还是少搞点吧
  • 调非常久(?),贴个代码吧
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=2e6+10;
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0' || ch>'9')
    {
        if (ch == '-') f=-1;
        ch=getchar();
    }
    while (ch>='0' && ch<='9')
    {
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int s,w;
struct _ {int x,y,a,id;}z[maxn],b[maxn];
bool cmp(_ A,_ B) {return A.x == B.x?A.y<B.y:A.x<B.x;}
int t;
struct BIT
{
    int c[maxn];
    int lowbit(int x) {return x&(-x);}
	void add(int x,int v) {if (x == 0) return ;while (x<=w) c[x]+=v,x+=lowbit(x);}
	int que(int x) {int res=0;while (x) res+=c[x],x-=lowbit(x);return res;}
}BT;
int ans[maxn];
void cdq(int l,int r)
{
    if (l == r) return;
    int mid=(l+r) >> 1;
    cdq(l,mid),cdq(mid+1,r);
//    sort(z+l,z+mid,cmp),sort(z+mid+1,z+r,cmp);
    int i=l,j=mid+1,k=l;
    while (i<=mid && j<=r)
	{
		if (z[i].x<=z[j].x)
        {
            if (z[i].a>0) BT.add(z[i].y,z[i].a);
            b[k++]=z[i++];
        }
        else
        {
            if (z[j].a == -1) ans[z[j].id]+=BT.que(z[j].y);
            if (z[j].a == -2) ans[z[j].id]-=BT.que(z[j].y);
            b[k++]=z[j++];
        }
	}
	while (j<=r)
    {
        if (z[j].a == -1) ans[z[j].id]+=BT.que(z[j].y);
        if (z[j].a == -2) ans[z[j].id]-=BT.que(z[j].y);
        b[k++]=z[j++];
    }
    for (int p=l;p<i;p++) if (z[p].a>0) BT.add(z[p].y,-z[p].a);
    while (i<=mid) b[k++]=z[i++];
    for (i=l;i<=r;i++) z[i]=b[i];
//    memset(BT.c,0,sizeof(BT.c));
}
int d;
signed main()
{
//    freopen("1.in","r",stdin);
//    freopen("1.out","w",stdout);
    s=read();w=read();
    while (1)
    {
        int op=read();if (op == 3) break;
        if (op == 2)
        {
            int x=read(),y=read(),xx=read(),yy=read();
            z[++t]={xx,yy,-1,++d};
            z[++t]={x-1,yy,-2,d};
            z[++t]={xx,y-1,-2,d};
            z[++t]={x-1,y-1,-1,d};
            ans[d]=(((x-1)*(y-1))-(xx*(y-1))-((x-1)*yy)+xx*yy)*s;
        }
		else
        {
            int x=read(),y=read(),a=read();
            z[++t]={x,y,a,0};
        }
    }
//    cerr << "chk:";cerr << z[1].a << " ";
//    cerr << z[1].y << " " << z[1].a <<endl;
    cdq(1,t);
    for (int i=1;i<=d;i++) printf("%lld\n",ans[i]);
    return 0;
}

例题P3157 [CQOI2011] 动态逆序对

  • 这是一个三维偏序,三维分别是:
    • 时间 \(t\)
    • 每个点的值 \(val\)
    • 每个点的编号 \(id\)
  • 时间我们可以通过输入时处理一下干掉
  • 我们发现比普通逆序对多一个删除,考虑删除一个点的影响范围
  • 显然是它的前面和后面(\(All\)
  • 这不好办,所以我们拆开算,一个利用 \(CDQ\) 使 \(id\) 降序,找比查的点小的,另一个反过来,利用 \(BIT\) 差分统计答案
  • 由于我们要求按时间排序以便干掉一维,因此我们可以把原序列的时间设为 \(0\),其他按输入顺序标记,原序列都使 \(BIT\)\(+1\),其余 \(-1\),这样可以保证时间不会造成影响
  • 讲得有点乱可以去翻翻题解
  • 对答案的统计需要做个减法
  • e,讲得很诡异,看代码吧
  • 不开 \(long\) \(long\) ___
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=1e6+10;
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0' || ch>'9')
    {
        if (ch == '-') f=-1;
        ch=getchar();
    }
    while (ch>='0' && ch<='9')
    {
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int n,m;
int pla[maxn];
struct _ {int t,v,id;}z1[maxn],b1[maxn],z2[maxn],b2[maxn];
struct BIT
{
    int c[maxn];
    int lowbit(int x) {return x&(-x);}
	void add(int x,int v) {while (x<=(int)(2e5)) c[x]+=v,x+=lowbit(x);}
	int que(int x) {int res=0;while (x) res+=c[x],x-=lowbit(x);return res;}
}BT;
int ans[maxn];
void cdq1(int l,int r)
{
    if (l == r) return;
    int mid=(l+r) >> 1;
    cdq1(l,mid),cdq1(mid+1,r);
    int i=l,j=mid+1,k=l;
    while (i<=mid && j<=r)
    {
        if (z1[i].id>=z1[j].id)
        {
            z1[i].t == 0?BT.add(z1[i].v,1):BT.add(z1[i].v,-1);
            b1[k++]=z1[i++];
        }
        else
        {
            ans[z1[j].t]+=BT.que(z1[j].v-1);
            b1[k++]=z1[j++];
        }
    }
    while (j<=r)
    {
        ans[z1[j].t]+=BT.que(z1[j].v-1);
        b1[k++]=z1[j++];
    }
    for (int p=l;p<i;p++) z1[p].t == 0?BT.add(z1[p].v,-1):BT.add(z1[p].v,1);
    while (i<=mid) b1[k++]=z1[i++];
    for (i=l;i<=r;i++) z1[i]=b1[i];
}
void cdq2(int l,int r)
{
    if (l == r) return;
    int mid=(l+r) >> 1;
    cdq2(l,mid),cdq2(mid+1,r);
    int i=l,j=mid+1,k=l;
    while (i<=mid && j<=r)
    {
        if (z2[i].id<=z2[j].id)
        {
            z2[i].t == 0?BT.add(z2[i].v,1):BT.add(z2[i].v,-1);
            b2[k++]=z2[i++];
        }
        else
        {
            ans[z2[j].t]+=BT.que((int)(2e5))-BT.que(z2[j].v);
            b2[k++]=z2[j++];
        }
    }
    while (j<=r)
    {
        ans[z2[j].t]+=BT.que((int)(2e5))-BT.que(z2[j].v);
        b2[k++]=z2[j++];
    }
    for (int p=l;p<i;p++) z2[p].t == 0?BT.add(z2[p].v,-1):BT.add(z2[p].v,1);
    while (i<=mid) b2[k++]=z2[i++];
    for (i=l;i<=r;i++) z2[i]=b2[i];
}
signed main()
{
    n=read(),m=read();
    for (int i=1;i<=n;i++)
    {
        int x=read();
        z1[i]=z2[i]={0,x,i};
        pla[x]=i;
    }
    for (int i=1;i<=m;i++)
    {
        int x=read();
        z1[i+n]=z2[i+n]={i,x,pla[x]};
    }
    cdq1(1,m+n),cdq2(1,m+n);
    for (int i=0;i<m;i++)
    {
        printf("%lld\n",ans[i]);
        ans[i+1]=ans[i]-ans[i+1];
    }
    return 0;
}

  • 通过上面的例题,我们发现了 \(CDQ\) 分治的确是基于时间的分治,也大概了解了具体维护方法

  • 我们再来一道

例题P4690 [Ynoi Easy Round 2016] 镜中的昆虫

  • on 26.7.27
  • 前置芝士:珂朵莉树
  • 这里看

从三维偏序到四维偏序

  • 我们搞了一下三维偏序,那么我们如何扩展呢
  • 欸,你发现其实 \(CDQ\) 分治是可以嵌套的
  • 我们知道利用 \(CDQ\)\(DS\) 可以干两维,那我们再加一个 \(CDQ\),就可以干三维了
  • 具体怎么做?
  • 首先,我们通过排序干一维
  • 然后,我们跑第一个 \(CDQ\),标记一下左右区间,打个 \(tag\),方便后面的第二次
  • 接下来就是第二次 \(CDQ\),这个就是常规的了,但是记得还要判一下是否打了标记
  • 口胡不太好理解,我们看例题

例题P14957 【模板】离线静态四维数点

  • 限制题目都说了,显然是四维偏序
  • 那么我们按上面说的搞
  • 先对 \(x_1\) 排序,就干一维了
  • 然后第一次 \(CDQ\),对左右区间打 \(tag\),并对 \(y_1\) 排序
  • 最后用第二次 \(CDQ\) 处理 \(x_2\),并用 \(BIT\) 维护 \(y_2\),在统计答案时还要判 \(tag\),如果有 \(tag\)(左区间)且是操作,再进行操作,查询同理
  • 给代码,看不懂排序的,e,我也没招
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=1e6+10;
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0' || ch>'9')
    {
        if (ch == '-') f=-1;
        ch=getchar();
    }
    while (ch>='0' && ch<='9')
    {
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int n,m;
int lshz[(int)(1e7)],tot;
int t,d;
struct _ {int xa,ya,xb,yb,op,tag,id;}z[maxn],b[maxn];
struct _________
{
    int c[maxn];
    int lowbit(int x) {return x&(-x);}
    void add(int x,int v) {while (x<=(int)(8e5)) c[x]+=v,x+=lowbit(x);}
    int que(int x)
    {
        int res=0;
        while (x)
        {
            res+=c[x];
            x-=lowbit(x);
        }
        return res;
    }
}BT;
int ans[maxn];
void cdq2(int l,int r)
{
    if (l == r) return;
    int mid=(l+r) >> 1;
    cdq2(l,mid),cdq2(mid+1,r);
    int i=l,j=mid+1,k=l;
    while (i<=mid && j<=r)
    {
        if (z[i].xb<=z[j].xb)
        {
           if (z[i].op && z[i].tag) BT.add(z[i].yb,1);
           b[k++]=z[i++];
        }
        else
        {
            if (!z[j].op && !z[j].tag) ans[z[j].id]+=BT.que(z[j].yb);
            b[k++]=z[j++];
        }
    }
    while (j<=r)
    {
        if (!z[j].op && !z[j].tag) ans[z[j].id]+=BT.que(z[j].yb);
        b[k++]=z[j++];
    }
    for (int p=l;p<i;p++) if (z[p].op && z[p].tag) BT.add(z[p].yb,-1);
    while (i<=mid) b[k++]=z[i++];
    for (i=l;i<=r;i++) z[i]=b[i];
}
void cdq1(int l,int r)
{
    if (l == r) return;
    int mid=(l+r) >> 1;
    cdq1(l,mid),cdq1(mid+1,r);
    for (int i=l;i<=mid;i++) z[i].tag=1;
    for (int i=mid+1;i<=r;i++) z[i].tag=0;
    sort(z+l,z+1+r,[](_ x,_ y){return x.ya==y.ya?x.xb==y.xb?x.yb==y.yb?x.op<y.op:x.yb<y.yb:x.xb<y.xb:x.ya<y.ya;});
    cdq2(l,r);
}
signed main()
{
    n=read(),m=read();
    for (int i=1;i<=n;i++)
    {
        int xa=read(),ya=read(),xb=-read(),yb=-read();
        lshz[++tot]=yb;
//        cerr << xa << " " << ya << " " << xb << " " << yb <<"-----" << endl;
        z[++t]={xa,ya,xb,yb,1,0,0};
    }
    for (int i=1;i<=m;i++)
    {
        int xa=read(),ya=read(),xb=-read(),yb=-read();
        lshz[++tot]=yb;
        z[++t]={xa,ya,xb,yb,0,0,++d};
    }
    sort(lshz+1,lshz+1+tot);int K=unique(lshz+1,lshz+1+tot)-lshz-1;
    for (int i=1;i<=t;i++) z[i].yb=lower_bound(lshz+1,lshz+1+K,z[i].yb)-lshz;
//    for (int i=1;i<=t;i++) cout << z[i].xa << " " <<z[i].ya << " " <<z[i].xb << " " <<z[i].yb <<endl;
    sort(z+1,z+1+n+m,[](_ x,_ y){return x.xa==y.xa?x.ya==y.ya?x.xb==y.xb?x.yb==y.yb?x.op<y.op:x.yb<y.yb:x.xb<y.xb:x.ya<y.ya:x.xa<y.xa;});
    cdq1(1,n+m);
    for (int i=1;i<=d;i++) printf("%lld\n",ans[i]);
    return 0;
}

\(CDQ\) 优化 \(DP\)

  • 为什么 \(CDQ\) 可以优化 \(DP\)
  • 有一些 \(DP\) 的转移的条件是偏序条件,可以利用 \(CDQ\) 优化转移过程,这个要结合例题看

例题P4093 [HEOI2016/TJOI2016] 序列

  • 我们思考一下,这东西长得很像最长不下降子序列,考虑 \(DP\)
  • 我们设计一下转移,设 \(f_i\) 表示以第 \(i\) 项结尾的最长不下降子序列长度
$f_j = \max\limits_{i < j}{f_i + 1} , (mx_i \le a_j , a_i \le mn_j)$
  • 其中,\(mx_i\) 表示所有 \(i\) 位置可能性的最大值,\(mn_i\) 表示所有 \(i\) 位置可能性的最小值
  • 我们观察这个转移,这是一个三维偏序,其中的三维分别为方程转移的限制
  • 首先 \(i<j\) 我们可以直接干掉,不管了,我们考虑利用 \(CDQ\) 优化
  • 但是!使用传统的 \(CDQ\) 并不能正确转移,因为 \(CDQ\) 是左区间和右区间同时往下递归的,下面说详细点

比如,你的区间是 \([1,4]\),你递归最后区间分别为 \([1,1]\)\([2,2]\)\([3,3]\)\([4,4]\),现在开始执行 \(CDQ\),先算 \([1,1]\)\([2,2]\) 做出的贡献,再算 \([3,3]\)\([4,4]\) 做出的贡献,你发现,woc \([3,3]\) 转移了滚木!

  • 所以我们要把 \(CDQ\) 分治变形,先递归左区间,进行代价计算,再递归右区间,这样左区间会一直转移完才会转移右区间,这就是对的
  • 我们继续考虑如何进行 \(CDQ\),由于我们分左右区间递归,所以对于上述 \(mx_i \le a_j\) 的限制,我们直接让当前区间的左半个按 \(mx\) 排序,右半个按 \(a\) 排序,限制 \(a_i \le mn_j\) 我们用 \(DS\) 处理,当 \(mx_i \le a_j\) 满足时,向 \(BIT\) 加入 \(a_i\),否则查询 \(mn_j\),让 \(f_j = max(f_j,query)\)
  • 注意是排序的范围以及左区间处理完后的 \(BIT\) 的复原及记得处理完把数组还原以及转移的下标问题
点击查看代码
#include<bits/stdc++.h>
using namespace std;
constexpr int maxn=1e6+10,inf=0x7f7f7f7f;
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0' || ch>'9')
    {
        if (ch == '-') f=-1;
        ch=getchar();
    }
    while (ch>='0' && ch<='9')
    {
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int n,m,f[maxn];
struct _ {int mx,mn,a,id;}z[maxn],b[maxn];
struct BIT
{
    int c[maxn];
    void init() {for (int i=1;i<=(int)(1e5);i++) c[i]=-inf;}
    int lb(int x) {return x&(-x);}
    void add(int x,int v)
    {
        while (x<=(int)(1e5))
        {
            c[x]=max(c[x],v);
            x+=lb(x);
        }
    }
    int getmx(int x)
    {
        int res=-inf;
        while (x)
        {
            res=max(res,c[x]);
            x-=lb(x);
        }
        return res;
    }
    void cle(int x)
    {
        while (x<=(int)(1e5))
        {
            c[x]=-inf;
            x+=lb(x);
        }
    }
}BT;
void cdq(int l,int r)
{
    if (l == r) return;
    int mid=(l+r) >> 1;
    cdq(l,mid);
    sort(z+l,z+mid+1,[](_ a,_ b){return a.mx<b.mx;});sort(z+mid+1,z+r+1,[](_ a,_ b){return a.a<b.a;});
    int i=l,j=mid+1,k=l;
    while (i<=mid && j<=r)
    {
        if (z[i].mx<=z[j].a)
        {
            BT.add(z[i].a,f[z[i].id]);
            b[k++]=z[i++];
        }
        else
        {
            f[z[j].id]=max(f[z[j].id],BT.getmx(z[j].mn)+1);
            b[k++]=z[j++];
        }
    }
    while (j<=r)
    {
        f[z[j].id]=max(f[z[j].id],BT.getmx(z[j].mn)+1);
        b[k++]=z[j++];
    }
    for (int p=l;p<i;p++) BT.cle(z[p].a);
    while (i<=mid) b[k++]=z[i++];
    for (i=l;i<=r;i++) z[i]=b[i];
    sort(z+l,z+r+1,[](_ a,_ b){return a.id<b.id;});
    cdq(mid+1,r);
}
int main()
{
    BT.init();
    n=read(),m=read();
    for (int i=1;i<=n;i++) f[i]=1;
    for (int i=1;i<=n;i++)
    {
        int x=read();
        z[i]={x,x,x,i};
    }
    for (int i=1;i<=m;i++)
    {
        int x=read(),v=read();
        z[x].mn=min(z[x].mn,v);
        z[x].mx=max(z[x].mx,v);
    }
//    for (int i=1;i<=n;i++) cerr << z[i].a << " " << z[i].mn << " " << z[i].mx <<endl;
    cdq(1,n);
    int ans=-inf;
    for (int i=1;i<=n;i++) ans=max(ans,f[i]);
    printf("%d\n",ans);
    return 0;
}

例题P5621 [DBOI2019] 德丽莎世界第一可爱

  • 虽然我调了 \(10\) 发,但是德丽莎是世界第一可爱

OIP-C

  • 并且我发现了一个调代码的方法:双线程调代码
    • 就是什么你写一份再复制一份,对着一份爆改,然后你就会发现这两份会有一份对的(— w — ||)
  • 我们发现貌似需要 \(DP\),但是方程很好设计就是了
  • 第一眼看题以为是五维偏序,其实是四维偏序,我们上面搞过
  • 我们仍然考虑利用 \(CDQ\) 优化 \(DP\) 的转移,优化方法与上面的例题一致,注意点也一致
  • 不过还有要注意的,就是在第一次 \(CDQ\) 时,也要把左右递归拆开,并且在 \(CDQ2\) 跑完后要按第一元素还原区间后再递归右子树
  • 代码全是细节,用 sort 好像会炸
  • 虽然我没当过多久舰长,但是我还是要说德丽莎是世界第一可爱
  • 代码甚至是隔夜的

批注 2026-07-22 095055

点击查看代码
#include<bits/stdc++.h>
#define sort stable_sort
using namespace std;
constexpr int maxn=1e6+10,inf=0x7f7f7f7f;
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0' || ch>'9')
    {
        if (ch == '-') f=-1;
        ch=getchar();
    }
    while (ch>='0' && ch<='9')
    {
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int n,f[maxn];
int s[maxn],t;
struct _ {int h,e,a,d,c,id,tag;}z[maxn],b[maxn];
struct BIT
{
    int c[maxn];
    void init() {for (int i=1;i<=(int)(1e5);i++) c[i]=-inf;}
    int lb(int x) {return x&(-x);}
    void add(int x,int v)
    {
        while (x<=(int)(1e5))
        {
            c[x]=max(c[x],v);
            x+=lb(x);
        }
    }
    int getmx(int x)
    {
        int res=-inf;
        while (x)
        {
            res=max(res,c[x]);
            x-=lb(x);
        }
        return res;
    }
    void cle(int x)
    {
        while (x<=(int)(1e5))
        {
            c[x]=-inf;
            x+=lb(x);
        }
    }
}BT;
void cdq2(int l,int r)
{
    if (l == r) return;
    int mid=(l+r) >> 1;
    cdq2(l,mid);
    sort(z+l,z+mid+1,[](_ a,_ b){return a.a == b.a?a.d == b.d?a.c>b.c:a.d<b.d:a.a<b.a;});
    sort(z+mid+1,z+r+1,[](_ a,_ b){return a.a == b.a?a.d == b.d?a.c>b.c:a.d<b.d:a.a<b.a;});
    int i=l,j=mid+1,k=l;
    while (i<=mid && j<=r)
    {
        if (z[i].a<=z[j].a)
        {
            if (z[i].tag) BT.add(z[i].d,f[z[i].id]);
            b[k++]=z[i++];
        }
        else
        {
            if (!z[j].tag) f[z[j].id]=max(f[z[j].id],BT.getmx(z[j].d)+z[j].c);
            b[k++]=z[j++];
        }
    }
    while (j<=r)
    {
        if (!z[j].tag) f[z[j].id]=max(f[z[j].id],BT.getmx(z[j].d)+z[j].c);
        b[k++]=z[j++];
    }
    for (int p=l;p<i;p++) BT.cle(z[p].d);
    while (i<=mid) b[k++]=z[i++];
    for (i=l;i<=r;i++) z[i]=b[i];
    sort(z+l,z+r+1,[](_ a,_ b){return a.e == b.e?a.a == b.a?a.d == b.d?a.c>b.c:a.d<b.d:a.a<b.a:a.e<b.e;});
    cdq2(mid+1,r);
}
void cdq1(int l,int r)
{
    if (l == r) return;
    int mid=(l+r) >> 1;
    cdq1(l,mid);
    for (int i=l;i<=mid;i++) z[i].tag=1;
    for (int i=mid+1;i<=r;i++) z[i].tag=0;
    sort(z+l,z+r+1,[](_ a,_ b){return a.e == b.e?a.a == b.a?a.d == b.d?a.c>b.c:a.d<b.d:a.a<b.a:a.e<b.e;});
    cdq2(l,r);
    sort(z+l,z+1+r,[](_ a,_ b){return a.h == b.h?a.e == b.e?a.a == b.a?a.d == a.d?a.c>b.c:a.d<b.d:a.a<b.a:a.e<b.e:a.h<b.h;});
    cdq1(mid+1,r);
}
int main()
{
    n=read();
    for (int i=1;i<=n;i++)
    {
        int x1=read(),x2=read(),x3=read(),x4=read(),x5=read();
        f[i]=x5;
        z[i]={x1,x2,x3,x4,x5,i,0};
        s[++t]=x4;
    }
    sort(s+1,s+1+n);t=unique(s+1,s+1+t)-s-1;
    for (int i=1;i<=n;i++) z[i].d=lower_bound(s+1,s+1+t,z[i].d)-s;
    sort(z+1,z+1+n,[](_ a,_ b){return a.h == b.h?a.e == b.e?a.a == b.a?a.d == a.d?a.c>b.c:a.d<b.d:a.a<b.a:a.e<b.e:a.h<b.h;});
    cdq1(1,n);
    int ans=-inf;
    for (int i=1;i<=n;i++) ans=max(ans,f[i]);
    printf("%d\n",ans);
    return 0;
}

后话

  • msjing要去搞主席树了,\(CDQ\) 分治先放着吧
posted @ 2026-07-21 17:28  msjing  阅读(17)  评论(2)    收藏  举报