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 ...
随机推荐
- UE4的编程C++创建一个FPSproject(两)角色网格、动画、HUD、子弹类
立即归还,本文将总结所有这些整理UE4有关角色的网络格.动画.子弹类HUD一个简单的实现. (五)角色加入网格 Character类为我们默认创建了一个SkeletaMeshComponent组件,所 ...
- uva 10069 Distinct Subsequences(高精度 + DP求解子串个数)
题目连接:10069 - Distinct Subsequences 题目大意:给出两个字符串x (lenth < 10000), z (lenth < 100), 求在x中有多少个z. ...
- 在JAVA中开发应用之html5离线应用
1.环境搭建(Tomcat为例): 在Tomcat中的conf配置文件中web.xml中添加离线配置: <!--HTML5--> <mime-mapping> <ext ...
- java中synchronized的使用方法与具体解释
Java语言的keyword.当它用来修饰一个方法或者一个代码块的时候,可以保证在同一时刻最多仅仅有一个线程运行该段代码. 一.当两个并发线程訪问同一个对象object中的这个synchronized ...
- Swift - 使用UIScrollView实现页面滚动切换
UIScrollView提供了以页面为单位滚动显示各个子页面内容的功能,每次手指滑动后会滚动一屏的内容. 要实现该功能,需要如下操作: 1,将UIScrollView的pagingEnabled属 ...
- C# split字符串 依据1个或多个空格
实例场景.对于字符串:"AAAA AAA BBBB BBB BBB CCCCCCCC". 1. 分隔为 "AAAA AAA" . & ...
- 14.4.3.1 The InnoDB Buffer Pool
14.4.3.1 The InnoDB Buffer Pool 14.4.3.2 Configuring Multiple Buffer Pool Instances 14.4.3.3 Making ...
- CF 514C(hash)
传送门:Watto and Mechanism 题意:输入a个字符串和b个待检测字符串.问待检测字符串是否可以由某个已知字符串改变且只改变一个字母得到. 分析:字符串hash,枚举待测字符串每一位进行 ...
- No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=arm64, VALID_ARCHS=armv7 armv7s)
问题: No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=arm64, VALID_ARCHS=armv7 armv ...
- 2013 吉林通化邀请赛 Play Game 记忆化搜索
dp[ba][ta][bb][tb]表示a堆牌从下面拿了ba张,从上面拿了ta张.b堆牌从下面拿了bb张,从上面拿了tb张.当前玩家能得到的最大的分数. 扩展方式有4种,ba+1,ta+1,bb+1, ...