首页 > 其他分享 >CDQ 分治

CDQ 分治

时间:2023-08-21 20:57:07浏览次数:43  
标签:le int 分治 mid maxn cdq include CDQ

定义

CDQ 分治作为一种思想,主要在解决形如 \([l,r]\) 区间中找符合要求的点对 \((i,j)\) 的问题时应用。具体的思想是:

  • 先递归解决 \([l,mid]\) 和 \([mid+1,r]\) 中的点对;
  • 再计算 \(l\le i\le mid<mid+1\le j\le r\) 的点对 \((i,j)\) 数量。

例题

例 1:P3810 【模板】三维偏序(陌上花开)

题意:给定长为 \(n\) 的数组 \(a,b,c\),令 \(f(i)\) 表示 \(a_j\le a_i,b_j\le b_i,c_j\le b_i\) 的 \(j\) 的数量,对于每个 \(d\in[0,n)\),求出满足 \(f(i)=d\) 的 \(i\) 的个数。

问题实质上可以转化为求出所有的 \(f(i)\)。对于 \(f(i)\),我们一维一维处理:先按照 \(a\) 升序排序,这样我们就要找出所有满足 \(b_j\le b_i,c_j\le c_i\) 的有序数对 \((j,i)\)(\(j<i\))的个数。对于剩下两维,我们使用 CDQ 分治。假设函数 cdq(l,r) 能够处理 \([l,r]\) 之间的问题,那么,算法流程如下:

  • 先调用 cdq(l,mid)cdq(mid+1,r)
  • 计算 \(l\le i\le mid<mid+1\le j\le r\) 的点对 \((i,j)\) 数量。

现在考虑设计算法解决后一个问题。我们可以枚举每一个 \(j\in[mid+1,r]\),计算符合条件的 \(i\)。先在 \([l,mid]\) 和 \([mid+1,r]\) 中分别按 \(b\) 排序,这样我们从小到大枚举 \(j\) 时,\(i\) 相对应的是 \([l,x]\) 的一段,且 \(x\) 随 \(j\) 增大而不降,这一点可以用双指针解决。然后我们要统计 \([l,x]\) 中 \(c_i<c_j\) 的个数,可以使用树状数组解决。

时间复杂度满足 \(T(n) = 2T(\frac{n}{2})+\mathcal{O}(n\log n)\),于是时间复杂度 \(T(n)=\mathcal{O}(n\log^2 n)\)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100005
#define maxk 200005
using namespace std;
int n,k,ans[maxn],cnt[maxn],num[maxn]; struct node{int a,b,c,cnt,id;}a[maxn]; bool cmp2(node aa,node bb){return aa.b<bb.b;}
bool cmp1(node aa,node bb){return aa.a==bb.a?(aa.b==bb.b?aa.c<bb.c:aa.b<bb.b):aa.a<bb.a;}
int c[maxk]; int lowbit(int x){return x&(-x);} void modify(int p,int ad){for(;p<=k;p+=lowbit(p)) c[p]+=ad;}
int query(int p){int res=0; for(;p>=1;p-=lowbit(p)) res+=c[p]; return res;}
void cdq(int l,int r){
	if(l==r) return; int mid=(l+r)>>1; cdq(l,mid); cdq(mid+1,r);
	sort(a+l,a+mid+1,cmp2); sort(a+mid+1,a+r+1,cmp2); int i=l,j=mid+1;
	for(;j<=r;j++){while(i<=mid&&a[i].b<=a[j].b) modify(a[i].c,a[i].cnt),i++; ans[a[j].id]+=query(a[j].c);}
	for(i--;i>=l;i--) modify(a[i].c,-a[i].cnt);
}
int main(){
	scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c),a[i].id=i; sort(a+1,a+1+n,cmp1);
	int pos=1; for(int i=2;i<=n;i++){
		if(a[i].a==a[i-1].a&&a[i].b==a[i-1].b&&a[i].c==a[i-1].c) a[pos].cnt++;
		else{a[pos]=(node){a[i-1].a,a[i-1].b,a[i-1].c,++a[pos].cnt,pos}; num[pos]=a[pos].cnt; pos++;}
		if(i==n){a[pos]=(node){a[n].a,a[n].b,a[n].c,++a[pos].cnt,pos}; num[pos]=a[pos].cnt;}
	} cdq(1,pos); for(int i=1;i<=pos;i++) cnt[ans[i]+num[i]-1]+=num[i]; for(int i=0;i<n;i++) printf("%d\n",cnt[i]); return 0;
}

标签:le,int,分治,mid,maxn,cdq,include,CDQ
From: https://www.cnblogs.com/qzhwlzy/p/17647051.html

相关文章

  • 点分树(动态点分治) 学习笔记
    模板题题目传送门给定一棵树(带点权),支持以下操作:修改点权。查询到一个点距离\(\lek\)的点的权值和。\(n,T\le10^5\)算法解析前置知识:点分治我们考虑把每次求出的重心和上一层的重心连边,我们就可以得到点分树。这棵树有以下性质:树高为\(\logn\),也就是暴力找LCA的......
  • CDQ分治
    如果现在有一些操作,有些操作会产生贡献,同时里面的情况会依次发生更改,要求我们去维护发生更改后的总贡献。这个问题会使得我们初感很棘手,主要原因在于这是一个动态的问题,当其中一个操作发生变化后会对很多的操作产生影响,导致寻常的数据结构难以维护。而现在引入的CDQ分治可以将一......
  • 分治算法——241. 为运算表达式设计优先级
    分治思路:对于一个算式来说,总是可以根据运算符分为左右两部分算式,接着分别计算结果并合并;每一个结果都是一个数组,包含这个算式的所有可能结果,计算时将左右两部分排列组合;递归的终点是字符串是纯数字(即分到一个算式中只剩下一个数字),直接返回。 比如示例中的2*3-4*5,有下面的......
  • 与点对有关的CDQ分治(菜鸟笔记)
    参考文章   首先要说明的是CDQ是一种思想,并且扩展范围很广。   这里主要说的是与点对有关的CDQ。参考文章说了与CDQ主要解决的三大类问题。第一类就是解决和点对有关的问题。主要是给定一个长度为n的序列,然后找出其中满足题意的点对\((i,j)\)。   CDQ的......
  • 分治算法C++
    1、光荣的梦想题目描述】Prince对他在这片陆上维护的秩序感到满意,于是决定启程离开艾泽拉斯。在他动身之前,Prince决定赋予King_Bette最强大的能量以守护世界、保卫这里的平衡与和谐。在那个时代,平衡是个梦想。因为有很多奇异的物种拥有各种不稳定的能量,平衡瞬间即被打破。KB决定求......
  • 浅谈根号分治
    浅谈根号分治一、问题引入  给定一个长度为\(n\)的序列,进行\(m\)次询问。每次询问给出两个数字\(x,y\)。对于每次询问,输出所有下标模除\(x\)等于\(y\)的元素的总和。  对于这个问题,我们发现他要维护的是一段离散的元素的和,而我们平时学的数据结构,如线段树等都只能维护一段......
  • 【学习笔记】线段树分治
    定义线段树分治是一种解决一类有插入、删除和整体查询操作的问题的方法。它是一种离线做法,通过在线段树上记录操作的时间区间来处理修改对询问的影响。每个操作被看作一个时间区间的修改,并在线段树上进行标记。然后通过深度优先搜索(DFS)依次执行这些操作,直到根节点来回答查询,并在......
  • 根号分治-2023牛客7 E-Star Wars
      也就是说对于大点和小点我们采用不同的方式维护对于大点来说我们只需要记录它的周围点的总和不需要知道具体的谁链接了它 对于小点我们需要维护它的所有信息他自己链接了哪些点 需要再开一个vector表示自己链接的大点这样大对大或者小对大的时候维护的信息也......
  • 【W的AC企划 - 第六期】树上分治
    往期浏览树上分治点分治讲解每次选取树的重心进行递归分治,中阶算法,大部分模板不需要理解,但是每一题都需要对维护函数进行修改。复杂度\(\mathcalO(N\logN)\)。个人封装由于需要进行一定程度的修改,不符合结构体封装的原则,故没有使用结构体。introot=0,MaxTree=1e1......
  • 点分治
    点分治模板1注意vis数组的作用,相当于把树切割注意变量sum的作用,第一次写的时候没用sum,全部用了n,导致没有正确找到树的重心,然后tle了#include<bits/stdc++.h>usingll=longlong;constintN=1E4+5,M=105,O=1E7+10;constintINF=......