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:何一维

- (\(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\) 项结尾的最长不下降子序列长度
- 其中,\(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\) 发,但是德丽莎是世界第一可爱

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

点击查看代码
#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\) 分治先放着吧

浙公网安备 33010602011771号