CDQ分治 三维偏序

这应该是一道CDQ分治的入门题目

我们知道,二维度的偏序问题直接通过,树状数组就可以实现了,但是三维如何实现呢?

我记得以前了解过一个小故事,应该就是分治的。

一个皇帝,想给部下分配任务,但是部下太多,他也无从下手于是他这个任务分给宰相,宰相也不怎么清楚,于是他又分给他的手下,这么一直分啊分啊,分到每一个人头顶上的时候

每个人知道自己要干什么,于是他把它的信息交给他的上级,上级有了这些数据后,他处理了交给他的上级。。。这么一直交啊。。。国王最后成功的分配这些任务。

CDQ分治也是一样,在这里,首先我们需要对X坐标进行排序,排序成功后,我们对区间进行分割,每次对区间进行二分,分成 [L - MID] [MID+1 - R]

我们分到底部的时候,实际上就是左右相等,于是只需要计算单个元素的效果,然后回到上一级,由于我们计算了两个区间的值,所以只需要计算 [MID+1 R] 对[L ,MID] 结果的影响即可

放到这道题,我们把X排序,然后枚举区间,对于【L,MID】对【MID+1,R】区间的贡献来说,应该如何计算呢?首先我们知道左边的区间的x肯定是小于右区间,我们其实已经可以不用

考虑X的影响了,所以我们对这连个区间对Y进行排序,那么我们要的三维偏序应该是,右边每一个值y,对应左边比右边的y小的值,我们把它的Z加入树状数组,像维护二维偏序一样维护

那么最后每个点贡献我们就可以得到,但是维护了每一段后,我们应该把树状数组进行清空,不然下次计算就有问题了。

注意要去重!!!

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#define LL long long
using namespace std;
const int maxx = 3e6+6;
struct node{
  int x,y,z,cnt,ans;
}flower[maxx];
int sum[maxx];
int num[maxx];
int n,k,tot;
int lowbit(int x){
  return x&(-x);
}
void add(int x,int w){
   for (int i=x;i<=k;i+=lowbit(i)){
      sum[i]+=w;
   }
}
int query(int x){
   int ans=0;
   for (int i=x;i;i-=lowbit(i)){
       ans+=sum[i];
   }
   return ans;
}
bool cmpy(node a,node b){
   if(a.y==b.y){
      if (a.z==b.z){
         return a.x<b.x;
      }
      return a.z<b.z;
   }
   return a.y<b.y;
}
bool cmpx(node a,node b){
   if(a.x==b.x){
      if (a.y==b.y){
        return a.z<b.z;
      }
      return a.y<b.y;
   }
   return a.x<b.x;
}
void cdq(int l,int r){
   if(l==r){
      flower[l].ans+=flower[l].cnt-1;
      return ;
   }
   int mid=(l+r)>>1;
   cdq(l,mid);
   cdq(mid+1,r);
   sort(flower+l,flower+mid+1,cmpy);
   sort(flower+mid+1,flower+r+1,cmpy);
   int j=l;
   for (int i=mid+1;i<=r;i++){
      while(j<=mid && flower[j].y<=flower[i].y){
          add(flower[j].z,flower[j].cnt);
          j++;
      }
      flower[i].ans+=query(flower[i].z);
   }
   for (int i=l;i<j;i++){
     add(flower[i].z,-flower[i].cnt);
   }
}
int main(){
  scanf("%d%d",&n,&k);
  for (int i=1;i<=n;i++){
     scanf("%d%d%d",&flower[i].x,&flower[i].y,&flower[i].z);
     flower[i].ans=1;
  }
  sort(flower+1,flower+1+n,cmpx);
  int tot=0;
  for (int i=1;i<=n;i++){
      if(i!=1 && flower[i].x==flower[i-1].x && flower[i].y==flower[i-1].y && flower[i].z==flower[i-1].z){
          flower[tot].cnt++;
      }else {
          tot++;
          flower[tot]=flower[i];
          flower[tot].cnt=1;
      }
  }
//  for (int i=1;i<=tot;i++){
//    cout<<flower[i].x<<" "<<flower[i].y<<" "<<flower[i].z<<" "<<flower[i].cnt<<" "<<flower[i].ans<<endl;
//  }
  cdq(1,tot);
  for (int i=1;i<=tot;i++){
     num[flower[i].ans]+=flower[i].cnt;
  }
  for (int i=1;i<=n;i++){
    printf("%d\n",num[i]);
  }
  return 0;
}
/*
1 2 2 1 1
2 2 1 1 1
2 2 3 2 1
2 3 3 0 1
3 2 1 2 1
3 2 2 0 1
3 3 3 0 1
*/

原文地址:https://www.cnblogs.com/bluefly-hrbust/p/11461302.html

时间: 2024-10-07 02:04:19

CDQ分治 三维偏序的相关文章

BZOJ 3262: 陌上花开 [CDQ分治 三维偏序]

Description 有n朵花,每朵花有三个属性:花形(s).颜色(c).气味(m),又三个整数表示.现要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量.定义一朵花A比另一朵花B要美丽,当且仅当Sa>=Sb,Ca>=Cb,Ma>=Mb.显然,两朵花可能有同样的属性.需要统计出评出每个等级的花的数量. Input 第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,000 ), 分别表示花的数量和最大属性值. 以下N行,每

【算法】CDQ分治 -- 三维偏序 &amp; 动态逆序对

初次接触CDQ分治,感觉真的挺厉害的. 整体思路即分而治之,再用之前处理出来的答案统计之后的答案. 大概流程是: 对于区间 l ~ r : 1.处理 l ~mid, mid + 1 ~ r 的答案 2.分别排序规整 3.计算 l ~ mid 中每一个数对 mid + 1 ~ r 中的答案的贡献, 累加 4.得到区间l ~ r的答案 CDQ分治我一共也才做了两道题目, 就一起整理在这里了.大体都差不多,CDQ+树状数组分别维护两个维度. 1.三维偏序 #include <bits/stdc++.h

BZOJ3262/洛谷P3810 陌上花开 CDQ分治 三维偏序 树状数组

原文链接http://www.cnblogs.com/zhouzhendong/p/8672131.html 题目传送门 - BZOJ3262 题目传送门 - 落谷P3810 题意 有$n$个元素,第$i$个元素有$a_i$.$b_i$.$c_i$三个属性,设$f(i)$表示满足$a_j\leq a_i$且$b_j\leq b_i$且$c_j\leq c_i$的$j$的数量.对于$d\in [0,n)$,求$f(i)=d$的数量. $n\leq 100000,max\{a_i,b_i,c_i|i

BZOJ.1935.[SHOI2007]Tree园丁的烦恼(CDQ分治 三维偏序)

题目链接 矩形查询可以拆成四个点的前缀和查询(树套树显然 但是空间不够) 每个操作表示为(t,x,y),t默认有序,对x分治,y用树状数组维护 初始赋值需要靠修改操作实现. //119964kb 4380ms #include <cstdio> #include <cctype> #include <algorithm> #define gc() getchar() #define lb(x) (x)&-(x) const int N=5e5+5; int n,

COGS 2479. [HZOI 2016]偏序 [CDQ分治套CDQ分治 四维偏序]

传送门 给定一个有n个元素的序列,元素编号为1~n,每个元素有三个属性a,b,c,求序列中满足i<j且ai<aj且bi<bj且ci<cj的数对(i,j)的个数. 对于100%的数据,1<=n<=50000,保证所有的ai.bi.ci分别组成三个1~n的排列. $CDQ$分治套$CDQ$分治也不是很难嘛 对于本题,设四维$a,b,c,d$ $Sort\ at\ a$ $CDQ(l,r)$ $\quad CDQ(l,mid)$ $\quad CDQ(mid+1,r)$ $\

P3157 [CQOI2011]动态逆序对 (CDQ解决三维偏序问题)

P3157 [CQOI2011]动态逆序对 题目描述 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. 输入格式 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. 输出格式 输出包含m行,依次为删除每个元素之前,逆序对的个数. 输入输出样例 输入

bzoj2683简单题 cdq分治

2683: 简单题 Time Limit: 50 Sec  Memory Limit: 128 MBSubmit: 1803  Solved: 731[Submit][Status][Discuss] Description 你有一个N*N的棋盘,每个格子内有一个整数,初始时的时候全部为0,现在需要维护两种操作: 命令 参数限制 内容 1 x y A 1<=x,y<=N,A是正整数 将格子x,y里的数字加上A 2 x1 y1 x2 y2 1<=x1<= x2<=N 1<

初学CDQ分治-NEU1702

关于CDQ分治,首先需要明白分治的复杂度. T(n) = 2T(n/2)+O(kn), T(n) = O(knlogn) T(n) = 2T(n/2)+O(knlogn), T(n) = O(knlog^2n) T(n) = 2T(n/2)+O(k), T(n) = O(kn) 那么我们要处理[l, r]内的询问,我们可以分别处理[l, m]和[m+1, r]的询问,然后以较小的复杂度计算出[l, m]对[m+1, r]的贡献. 最简单的cdq就是三维偏序问题. 两点(x1, y1, z1)和(

【算法】CDQ分治初探

CDQ分治是处理数据结构题的有力武器,通俗的讲,它可以替代一层数据结构,从而达到降低代码难度以及常数的作用,缺点是必须离线. CDQ分治一般可以用来处理偏序问题以及斜率优化DP问题. 与普通分治不同的是,CDQ分治左区间的答案对右区间有贡献,最经典的例子是归并排序求逆序对. 下面先讲讲偏序问题: 二维偏序本质上和归并排序求逆序对一样,不多提及. 三维偏序:第一维直接排序,第二维用CDQ分治,第三维用树状数组. 具体地讲讲CDQ分治求偏序.因为我们第一维已经排序了,那么绝对不会出现左区间的第一维比