hdu5618 (三维偏序,cdq分治)

时间:2023-02-04 23:19:48

给定空间中的n个点,问每个点有多少个点小于等于自己。

先来分析简单的二维的情况,那么只要将x坐标排序,那么这样的问题就可以划分为两个子问题,,这样的分治有一个特点,即前一个子问题的解决是独立的,而后一个子问题的解决依赖于前一个子问题,即用前一个子问题来解决后一个子问题,而不是合并。 这就是cdq分治。

具体的代码如下。

void cdq(int l, int r){
if(l==r) return;
int m = (l+r)>>;
cdq(l,m);
cdq(m+,r);
//按y进行排序,那么问题就变成两个y递增的集合,
//后一个集合中的每个y在前一个集合中有多少个y小于等于它
sort(a+l,a+m+,cmp);
sort(a+m+,a+r+,cmp);
int j = l;
for(int i=m+;i<=r;++i){
for(;j<=m;&&a[j].y<=a[i].y;++j);
a[i].sum += j - l;
}
}

而三维的问题由于多了一维,不能使用线性的方法 了。

我们可以用树状数组来维护z这一维,具体的代码如下。

并且最后要注意坐标相等的情况。

第一份代码,因为cdq里面又嵌套了sort,所以时间复杂度是O(n*logn*logn)

#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <string.h>
using namespace std;
struct Point{
int x,y,z;
int id;
int sum;
Point(){}
Point(int x, int y):x(x),y(y){}
bool operator<(const Point&rhs)const{
if(x!=rhs.x) return x < rhs.x;
if(y!=rhs.y) return y < rhs.y;
return z < rhs.z;
}
bool operator==(const Point &rhs)const{
return x==rhs.x && y==rhs.y && z==rhs.z;
}
};
bool cmp(const Point &lhs, const Point &rhs){
if(lhs.y!=rhs.y) return lhs.y <rhs.y;
return lhs.z <rhs.z;
}
const int N = + ;
Point a[N];
class BIT{
public:
int sum[N];
int n;
void init(){
n = ;
memset(sum,,sizeof(sum));
}
int lowbit(int x){
return x & (-x);
}
int modify(int x, int val){
while(x<=n){
sum[x] += val;
x += lowbit(x);
}
}
int getSum(int x){
int ret= ;
while(x>){
ret += sum[x];
x -= lowbit(x);
}
return ret;
}
}bit; void cdq(int l, int r){
if(l==r)return;
int m = (l+r)>>;
cdq(l,m);
cdq(m+,r);
sort(a+l,a+m+,cmp);
sort(a+m+,a+r+,cmp);
int j = l;
for(int i=m+;i<=r;++i){
for(;j<=m &&a[j].y<=a[i].y;++j)
bit.modify(a[j].z,);
a[i].sum += bit.getSum(a[i].z);
}
for(int i=l; i<j; ++i)
bit.modify(a[i].z,-); } int ans[N];
int main(){
int t,n;
scanf("%d",&t);
while(t--){ scanf("%d",&n);
for(int i=;i<n;++i){ scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z); a[i].id = i; a[i].sum=;}
sort(a,a+n);
bit.init();
cdq(,n-);
sort(a,a+n);
for(int i=;i<n;){
int j = i + ;
int tmp = a[i].sum;
//分治时,坐标相等的时候,
//排在前边的坐标不能使用后边的坐标更新自己,所以要在这里处理一下
for(;j<n &&a[i]==a[j];++j) tmp = max(tmp,a[j].sum);
for(int k=i;k<j;++k) ans[a[k].id] = tmp; i = j;
}
for(int i=;i<n;++i)
printf("%d\n",ans[i]);
}
return ;
}

第二份代码,在cdq分治的最后加入归并排序,是的复杂度变成O(n*logn)

#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <string.h>
using namespace std;
struct Point{
int x,y,z;
int id;
int sum;
Point(){}
Point(int x, int y):x(x),y(y){}
bool operator<(const Point&rhs)const{
if(x!=rhs.x) return x < rhs.x;
if(y!=rhs.y) return y < rhs.y;
return z < rhs.z;
}
bool operator==(const Point &rhs)const{
return x==rhs.x && y==rhs.y && z==rhs.z;
}
};
bool cmp(const Point &lhs, const Point &rhs){
if(lhs.y!=rhs.y) return lhs.y <rhs.y;
return lhs.z <rhs.z;
}
const int N = + ;
Point a[N];
class BIT{
public:
int sum[N];
int n;
void init(){
n = ;
memset(sum,,sizeof(sum));
}
int lowbit(int x){
return x & (-x);
}
int modify(int x, int val){
while(x<=n){
sum[x] += val;
x += lowbit(x);
}
}
int getSum(int x){
int ret= ;
while(x>){
ret += sum[x];
x -= lowbit(x);
}
return ret;
}
}bit; Point tmp[N];
void cdq(int l, int r){
if(l==r)return;
int m = (l+r)>>;
cdq(l,m);
cdq(m+,r);
//sort(a+l,a+m+1,cmp);
//sort(a+m+1,a+r+1,cmp);
int j = l;
for(int i=m+;i<=r;++i){
for(;j<=m &&a[j].y<=a[i].y;++j)
bit.modify(a[j].z,);
a[i].sum += bit.getSum(a[i].z);
}
for(int i=l; i<j; ++i)
bit.modify(a[i].z,-); //归并排序, 这样就不需要上面的sort了
int i = l ;
j = m+;
for(int k=l;k<=r;++k){
if(i>m) tmp[k] = a[j++];
else if(j>r) tmp[k] = a[i++];
else if(a[i].y < a[j].y) tmp[k] = a[i++];
else tmp[k] = a[j++];
}
for(int k=l;k<=r;++k)
a[k] = tmp[k]; } int ans[N];
int main(){
int t,n;
scanf("%d",&t);
while(t--){ scanf("%d",&n);
for(int i=;i<n;++i){ scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z); a[i].id = i; a[i].sum=;}
sort(a,a+n);
bit.init();
cdq(,n-);
sort(a,a+n);
for(int i=;i<n;){
int j = i + ;
int tmp = a[i].sum;
//分治时,坐标相等的时候,
//排在前边的坐标不能使用后边的坐标更新自己,所以要在这里处理一下
for(;j<n &&a[i]==a[j];++j) tmp = max(tmp,a[j].sum);
for(int k=i;k<j;++k) ans[a[k].id] = tmp; i = j;
}
for(int i=;i<n;++i)
printf("%d\n",ans[i]);
}
return ;
}

具体算法流程如下:

1.将整个操作序列分为两个长度相等的部分(分)

2.递归处理前一部分的子问题(治1)

3.计算前一部分的子问题中的修改操作对后一部分子问题的影响(治2)

4.递归处理后一部分子问题(治3)

而且如果需要分治完后数据要求有序,那么就可以在分治的最后加入归并排序等手段。

何时使用cdq分治:①如果一个问题的解决需要去循环判断,且这样的问题有很多, 那么就看看能不能分治,减少计算量,从小减小复杂度。

hdu5618 (三维偏序,cdq分治)的更多相关文章

  1. Luogu 3810 &amp&semi; BZOJ 3262 陌上花开&sol;三维偏序 &vert; CDQ分治

    Luogu 3810 & BZOJ 3263 陌上花开/三维偏序 | CDQ分治 题面 \(n\)个元素,每个元素有三个值:\(a_i\), \(b_i\) 和 \(c_i\).定义一个元素的 ...

  2. bzoj3262&colon; 陌上花开 三维偏序cdq分治

    三维偏序裸题,cdq分治时,左侧的x一定比右侧x小,然后分别按y排序,对于左侧元素按y大小把z依次插入到树状数组里,其中维护每个左侧元素对右侧元素的贡献,在bit查询即可 /************* ...

  3. &lbrack;bzoj&rsqb; 3263 陌上花开 洛谷 P3810 三维偏序&vert;&vert; CDQ分治 &amp&semi;&amp&semi; CDQ分治讲解

    原题 定义一个点比另一个点大为当且仅当这个点的三个值分别大于等于另一个点的三个值.每比一个点大就为加一等级,求每个等级的点的数量. 显然的三维偏序问题,CDQ的板子题. CDQ分治: CDQ分治是一种 ...

  4. BZOJ3262 陌上花开 —— 三维偏序 CDQ分治

    题目链接:https://vjudge.net/problem/HYSBZ-3262 3262: 陌上花开 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit ...

  5. BZOJ 3295:&lbrack;Cqoi2011&rsqb;动态逆序对(三维偏序 CDQ分治&plus;树状数组)

    http://www.lydsy.com/JudgeOnline/problem.php?id=3295 题意:简单明了. 思路:终于好像有点明白CDQ分治处理三维偏序了.把删除操作看作是插入操作,那 ...

  6. 三维偏序&lbrack;cdq分治学习笔记&rsqb;

    三维偏序 就是让第一维有序 然后归并+树状数组求两维 cdq+cdq不会 告辞 #include <bits/stdc++.h> // #define int long long #def ...

  7. 洛谷P3810-陌上开花&lpar;三维偏序&comma; CDQ&comma; 树状数组&rpar;

    链接: https://www.luogu.org/problem/P3810#submit 题意: 一个元素三个属性, x, y, z, 给定求f(b) = {ax <= bx, ay &lt ...

  8. COGS 2479&period; &lbrack;HZOI 2016&rsqb;偏序 &lbrack;CDQ分治套CDQ分治 四维偏序&rsqb;

    传送门 给定一个有n个元素的序列,元素编号为1~n,每个元素有三个属性a,b,c,求序列中满足i<j且ai<aj且bi<bj且ci<cj的数对(i,j)的个数. 对于100%的 ...

  9. BZOJ 2716&sol;2648 SJY摆棋子 &lpar;三维偏序CDQ&plus;树状数组&rpar;

    题目大意: 洛谷传送门 这明明是一道KD-Tree,CDQ分治是TLE的做法 化简式子,$|x1-x2|-|y1-y2|=(x1+y1)-(x2+y2)$ 而$CDQ$分治只能解决$x1 \leq x ...

随机推荐

  1. jsonp帮助你知道你关注的他或她喜欢什么歌曲

    利用腾讯提供的QQ音乐API,返回一段对方在QQ音乐收藏的歌曲名称json数据,并对该json做解析,就能知道你的那个他或她喜欢听什么歌曲了,然后你就知道他/她的品位了,然后就自己看着办了,嘿嘿.我只 ...

  2. webuploader跨域上传

    浏览器在跨域请求前会发个options请求来验证是否跨域,所以后端再处理这个options请求时,要告诉浏览器一些信息. 也就是个header信息 header("Access-Contro ...

  3. 【POJ】2449 Remmarguts&&num;39&semi; Date(k短路)

    http://poj.org/problem?id=2449 不会.. 百度学习.. 恩. k短路不难理解的. 结合了a_star的思想.每动一次进行一次估价,然后找最小的(此时的最短路)然后累计到k ...

  4. error C2783&colon; 无法为&OpenCurlyDoubleQuote;T”推导 模板 参数

    原则:“模板参数推导机制无法推导函数的返回值类型” 版本一: // 缺少<T> 参数 int n 对比第三个版本( 缺少<T> 参数 T n) ! 编译错误提示: 错误 1 e ...

  5. reactor设计模式

    reactor介绍 reactor的工作模式就像它的名字一样,是一种反射模式,当事件发生时,根据发生的事件调用注册的处理器. Reactor的优点和应用 Reactor最常用于非阻塞的socket 传 ...

  6. SQLyog MySQL GUI 11&period;13 Ultimate 中文破解版【转载】

    SQLyog是一个易于使用的.快速而简洁的图形化管理MYSQL数据库的工具,它能够在任何地点有效地管理你的数据库! SQLyog MySQL GUI是我常用的一个桌面工具,功能强大,让你有使用MSSQ ...

  7. MKMapView移动事件地图

    MKMapView移动事件地图 by 吴雪莹 -(void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated { ...

  8. php - preg&lowbar;match

    任务:匹配一个函数名或者变量名,如果碰到alpha,numeric,_以外的全部不允许通过. 实验1: <?php //第一个字符不符合就直接退出正则匹配 $str = '%abcscript% ...

  9. LeetCode 2

    No1 Given a sorted array and a target value, return the index if the target is found. If not, return ...

  10. Java语言基础之数组

    引出数组和数组的定义 为什么要使用数组: 问题一: 声明变量时,每一个单独的变量都要对应一个变量名,但现在要处理一组相同类型的数据时,如要表示班上100个人的年纪,绝不能定义100个变量来表示每一个人 ...