三维偏序:CDQ分治

cdq分治是一种常用的降维手段,可以解决偏序问题。

题目

给定$n$个三元组$(x, y, z)$,给定一个$f(a)$,表示所有元素$b$(自己不算),它的$x,y,z$均小于等于$a$的对应$x,y,z$,求$[0, n)$中每种$f$值的个数。

$n \leq 100000$

$x, y, z \leq 200000$

简单模型

一维:仅有$x$:按$x$排序即可。

二维:有$(x, y)$,按先$x$后$y$顺序排序,然后将$y$值用树状数组统计。

三维偏序:cdq分治解法

思想:类似归并排序

现将元素按$x, y, z$升序排序。(降一维$x$),然后还剩下两维,可以用归并排序的思想消去一维。

将待处理的序列分成两份,分别按$y$值排序和统计。(分治思想,类似逆序对)

统计:两部分中:左半部分$x$都比右半部分的小,因此只需要统计右半部分对左半部分的贡献即可。用树状数组统计$z$值。因为两边$y$值都是有序的,所以维护一个左边的指针,每次将$y$值小于等于右边当前位置的元素扔进树状数组统计,然后统计$z$值比它小的即可。

int p = l; //前指针
for(int i=mid+1; i<=r; i++){ //后半部分用前半部分更新
    for(;a[p].y<=a[i].y && p<=mid; ++p) upd(a[p].z, a[p].cnt); //把y比它小的加进树状数组,注意cnt
    a[i].ans += ask(a[i].z); //查询x, y, z均比a[i]小的
}

时间复杂度:$O(n log ^ 2 n)$

注意事项

  1. 处理前必须去重,因为是小于等于,要防止同样的元素被切进两个区域导致无法统计答案。最后统计答案时,要针对$cnt$再进行更改(详见代码)
  2. 不能每次新建一个树状数组或清空树状数组,要一步一步撤销操作,否则每次都要花费$O(n)$时间清空,总时间复杂度变成$O(n^2)$。

Code

已经省略快读,快输

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100005, K = 200005;
typedef long long ll;
struct elem{
    int x, y, z, ans, cnt;
}a[N], tmp[N];
int ans[N];
bool cmpx(elem a, elem b){return (a.x == b.x)?((a.y==b.y)?(a.z<b.z):(a.y<b.y)):(a.x<b.x);} //先x后y,z排序
bool cmpy(elem a, elem b){return (a.y == b.y)?(a.z<b.z):(a.y<b.y);} //先y后z排序
int n, k;
int tr[K];
inline void upd(int p, int x){ //树状数组更新
    if(p == 0) return ;
    for(;p<=k; p+=p&-p) tr[p] += x;
}
inline int ask(int p){ //树状数组前缀和
    int ans = 0;
    for(;p; p-=p&-p) ans+=tr[p];
    return ans;
}

void solve(int l, int r){ //cdq分治
    if(l == r) return ;
    int mid = (l + r) >> 1;
    solve(l, mid); solve(mid+1, r); //两边排序,统计
    int p = l; //前指针
    for(int i=mid+1; i<=r; i++){ //后半部分用前半部分更新
        for(;a[p].y<=a[i].y && p<=mid; ++p) upd(a[p].z, a[p].cnt); //把y比它小的加进树状数组,注意cnt
        a[i].ans += ask(a[i].z); //查询x, y, z均比a[i]小的
    }
    for(int i=l; i<p; i++) upd(a[i].z, -a[i].cnt); //清空,注意没有用的不清空
    merge(a+l, a+mid+1, a+mid+1, a+r+1, tmp, cmpy); //归并,同归并排序
    for(int i=l; i<=r; i++) a[i] = tmp[i - l];
}

int main(){
    freopen("3810.in", "r", stdin);
    freopen("3810.out", "w", stdout);
    in(n); in(k);
    for(int i=1; i<=n; i++) in(a[i].x), in(a[i].y), in(a[i].z), a[i].cnt = 1;
    sort(a+1, a+1+n, cmpx);
    int p = 1;
    for(int i=2; i<=n; i++){ //去重,统计cnt
        if(a[i].x == a[i-1].x && a[i].y == a[i-1].y && a[i].z == a[i-1].z) ++a[p].cnt;
        else a[++p] = a[i];
    }
    solve(1, p);
    for(int i=1; i<=p; i++) ans[a[i].ans + a[i].cnt - 1] += a[i].cnt;//有相同的答案会增加,注意自己不算
    for(int i=0; i<n; i++) write(ans[i]), putchar(‘\n‘);
    return 0;
}

原文地址:https://www.cnblogs.com/RiverHamster/p/cdq-3dPartialOrder.html

时间: 2024-11-06 07:30:47

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

BZOJ 3295:[Cqoi2011]动态逆序对(三维偏序 CDQ分治+树状数组)

http://www.lydsy.com/JudgeOnline/problem.php?id=3295 题意:简单明了. 思路:终于好像有点明白CDQ分治处理三维偏序了.把删除操作看作是插入操作,那么可以按照插入的时间顺序看作是一维x,插入的数在原本序列的下标是一维y,插入的数本身是一维z.那么问题可以转化成每插入一个数(xx,yy,zz),求有多少个数(x,y,z)使得 x < xx,y < yy,z > zz .一开始先对 x 进行排序,然后进行CDQ分治.这样可以干掉一维,保证随

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)$ $\

51nod 1376: 最长递增子序列的数量(二维偏序+cdq分治)

1376 最长递增子序列的数量 Time Limit: 1 Sec Memory Limit: 128MB 分值: 160 难度:6级算法题 Description 数组A包含N个整数(可能包含相同的值).设S为A的子序列且S中的元素是递增的,则S为A的递增子序列.如果S的长度是所有递增子序列中最长的,则称S为A的最长递增子序列(LIS).A的LIS可能有很多个.例如A为:{1 3 2 0 4},1 3 4,1 2 4均为A的LIS.给出数组A,求A的LIS有多少个.由于数量很大,输出Mod 1

hdu 4742 Pinball Game 3D(三维LIS&amp;cdq分治&amp;BIT维护最值)

Pinball Game 3D Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 688    Accepted Submission(s): 276 Problem Description RD is a smart boy and excel in pinball game. However, playing common 2D p

三维偏序 cdq

luogu_3810 就是将逆序对转化到了三维上去 原理等我寒假再补 第一维sort解决 第二维并归排序(cdq)解决 第三维树状数组 // luogu-judger-enable-o2 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using std::sort; const int maxn=101000; const int Max=20100

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行,每

HDU 5618:Jam&#39;s problem again(CDQ分治+树状数组处理三维偏序)

http://acm.hdu.edu.cn/showproblem.php?pid=5618 题意:-- 思路:和NEUOJ那题一样的.重新写了遍理解了一下,算作处理三维偏序的模板了. 1 #include <cstdio> 2 #include <algorithm> 3 #include <iostream> 4 #include <cstring> 5 using namespace std; 6 #define INF 0x3f3f3f3f 7 #d

SPOJ LIS2 Another Longest Increasing Subsequence Problem 三维偏序最长链 CDQ分治

Another Longest Increasing Subsequence Problem Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=19929 Description Given a sequence of N pairs of integers, find the length of the longest incre

【算法】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