hdu5618 (三维偏序,cdq分治)
给定空间中的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分治)的更多相关文章
- Luogu 3810 & BZOJ 3262 陌上花开/三维偏序 | CDQ分治
Luogu 3810 & BZOJ 3263 陌上花开/三维偏序 | CDQ分治 题面 \(n\)个元素,每个元素有三个值:\(a_i\), \(b_i\) 和 \(c_i\).定义一个元素的 ...
- bzoj3262: 陌上花开 三维偏序cdq分治
三维偏序裸题,cdq分治时,左侧的x一定比右侧x小,然后分别按y排序,对于左侧元素按y大小把z依次插入到树状数组里,其中维护每个左侧元素对右侧元素的贡献,在bit查询即可 /************* ...
- [bzoj] 3263 陌上花开 洛谷 P3810 三维偏序|| CDQ分治 && CDQ分治讲解
原题 定义一个点比另一个点大为当且仅当这个点的三个值分别大于等于另一个点的三个值.每比一个点大就为加一等级,求每个等级的点的数量. 显然的三维偏序问题,CDQ的板子题. CDQ分治: CDQ分治是一种 ...
- BZOJ3262 陌上花开 —— 三维偏序 CDQ分治
题目链接:https://vjudge.net/problem/HYSBZ-3262 3262: 陌上花开 Time Limit: 20 Sec Memory Limit: 256 MBSubmit ...
- BZOJ 3295:[Cqoi2011]动态逆序对(三维偏序 CDQ分治+树状数组)
http://www.lydsy.com/JudgeOnline/problem.php?id=3295 题意:简单明了. 思路:终于好像有点明白CDQ分治处理三维偏序了.把删除操作看作是插入操作,那 ...
- 三维偏序[cdq分治学习笔记]
三维偏序 就是让第一维有序 然后归并+树状数组求两维 cdq+cdq不会 告辞 #include <bits/stdc++.h> // #define int long long #def ...
- 洛谷P3810-陌上开花(三维偏序, CDQ, 树状数组)
链接: https://www.luogu.org/problem/P3810#submit 题意: 一个元素三个属性, x, y, z, 给定求f(b) = {ax <= bx, ay < ...
- COGS 2479. [HZOI 2016]偏序 [CDQ分治套CDQ分治 四维偏序]
传送门 给定一个有n个元素的序列,元素编号为1~n,每个元素有三个属性a,b,c,求序列中满足i<j且ai<aj且bi<bj且ci<cj的数对(i,j)的个数. 对于100%的 ...
- BZOJ 2716/2648 SJY摆棋子 (三维偏序CDQ+树状数组)
题目大意: 洛谷传送门 这明明是一道KD-Tree,CDQ分治是TLE的做法 化简式子,$|x1-x2|-|y1-y2|=(x1+y1)-(x2+y2)$ 而$CDQ$分治只能解决$x1 \leq x ...
随机推荐
- ORACLE 五种表的优缺点总结
ORACLE 五种表的优缺点总结: 1.普通表(heap table):适合大部分设计场景,有长处也有缺点. 长处: a,语法简单方便 b,适合大部分场景 缺点: a,更新日志开销较大 b,Delet ...
- Xamarin.forms 自定义tabview控件
一 问题描述 forms本身ui代码是翻译为平台原生代码,forms按照xaml技术进行对android和ios两种ui模型进行公共抽象出了几种page和view,在空杯博客已经有详细介绍 http: ...
- Hadoop 的常用组件一览
Hadoop 集群安装及原理:hdfs命令行操作:Java操作hdfs的常用API接口:动态添加删除数据节点. HBase 集群安装及原理:Hbase命令行操作:Java操作Hbase的常用API接口 ...
- 【Node.js 自己封装的库 http_parse, libuv】
[Node.js 自己封装的库 http_parse, libuv] Node.js 介绍:一个网络框架,更多:http://www.oschina.net/p/nodejs 官网:http://no ...
- 大话项目管理工具之Confluence篇
前言 前面的两篇文章,讲的都是代码层次上的项目管理工具.这篇文章打算介绍一下关于知识管理的工具,当然,它也属于项目管理中的一部分.说到知识管理,你想到了什么呢?织网?找节点?不错,这是个人的知识管理. ...
- linux 怎么使用一个c/c++库
linux 怎么使用一个c/c++库 使用一个库,库有动态和静态之分. 静态库只要能够编译通过,运行时就没有提示缺少这个库可能,因为把这个库就是这个使用者的一部分了.动态库.两部分.1 库信息的注册 ...
- IBinder在进程之间传递一个对象的形式(一)
主张 什么时候service通常被称为远程时的,用到aidl来定一个接口供service和client来使用,这个事实上就是使用Binder机制的IPC通信.当client bind service成 ...
- cURL安装和使用笔记
0.前言 cURL是一个利用URL语法在命令行下工作的文件传输工具.它支持文件上传和下载,所以是综合传输工具,但习惯称cURL为下载工具.cURL还包含了用于程序开发的libcurl.cURL ...
- codeforces 598D Igor In the Museum
题目链接:http://codeforces.com/problemset/problem/598/D 题目分类:dfs 题目分析:处理的时候一次处理一片而不是一个,不然会超时 代码: #includ ...
- 深刻:截获windows的消息并分析实例(DefWindowProc),以WM_NCHITTEST举例(Windows下每一个鼠标消息都是由 WM_NCHITTEST 消息产生的,这个消息的参数包含了鼠标位置的信息)
1,回调函数工作机制 回调函数由操作系统自动调用,回调函数的返回值当然也是返回给操作系统了. 2,截获操作系统发出的消息,截获到后,将另外一个消息返回给操作系统,已达到欺骗操作系统的目的. 下面还是以 ...