三维偏序/cdq分治/

cdq分治概述

前置知识:(如果不懂要先去了解分治)
> 分治: 
>     分而治之,将原问题不断划分为若干个子问题,
>     直到子问题规模足够小可以直接解决子问题间互相独立且与原问题形式相同,
>     递归求解这些子问题,然后将各子问题的解合并得到原问题的解

cdq 分治和dp一样,是一种思想,因此 cdq 分治涵盖的范围相当的广泛,由于这样的思路最早是被陈丹琦引入国内的,所以就叫 cdq 分治了。
cdq分治可解决的问题大致可分为三类:

  • 与点对有关的问题(三维偏序)
  • cdq 分治优化 1D/1D 动态规划的转移(还不懂
  • 通过 cdq 分治,将一些动态问题转化为静态问题

本篇博客主要介绍和总结用cqd分治解决的三维偏序问题

二维偏序概述

三维偏序,听起来似乎是个排序,三维偏序是在二维偏序的基础上进行的。
好了,那 二维偏序又是个啥??
二维偏序
给你一个长度为n的序列,每个序列都有a,b两种属性,让你求具有某些关系的点对(i,j)个数。一般思路是先确定一维的顺序,在此基础上用树状数组维护第二维。

二维偏序例题分析

hdu1541-----> 戳戳

Problem Description
Astronomers often examine star maps where stars are represented by points on a plane and each star has Cartesian coordinates. Let the level of a star be an amount of the stars that are not higher and not to the right of the given star. Astronomers want to know the distribution of the levels of the stars.

For example, look at the map shown on the figure above. Level of the star number 5 is equal to 3 (it’s formed by three stars with a numbers 1, 2 and 4). And the levels of the stars numbered by 2 and 4 are 1. At this map there are only one star of the level 0, two stars of the level 1, one star of the level 2, and one star of the level 3.

You are to write a program that will count the amounts of the stars of each level on a given map.

Input
The first line of the input file contains a number of stars N (1<=N<=15000). The following N lines describe coordinates of stars (two integers X and Y per line separated by a space, 0<=X,Y<=32000). There can be only one star at one point of the plane. Stars are listed in ascending order of Y coordinate. Stars with equal Y coordinates are listed in ascending order of X coordinate.

Output
The output should contain N lines, one number per line. The first line contains amount of stars of the level 0, the second does amount of stars of the level 1 and so on, the last line contains amount of stars of the level N-1.

Sample Input

5
1 1
5 1
7 1
3 3
5 5

Sample Output

1
2
1
1
0

题意:有n个星星,输入时是以从下到上,从左到右的方式输入(x,y)的值。每个星星左下方有多少个星星就评多少等级,也就是求对于每个星星 i,有多少个 j 满足 xi<xj , yi<yj 。

首先我们可以按照 x 坐标排序,发现对于 i 来说 大于 i 的点已经不在答案的贡献里了。那么答案就是在排序后的数组中找 1∼i-1有几个元素 y 比 yi 小。
那么我们直接树状数组即可。

#include <bits/stdc++.h>
#define lowbit(x) ((x)&(-(x)))
using namespace std;
const int maxn=100000+10;
int n,c[maxn],ans[maxn];
struct Stars{
    int x,y;
}a[maxn];

bool cmp(Stars a,Stars b){
    if(a.x!=b.x) return a.x<b.x;
    return a.y<b.y;
}

inline int read(){
    register int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return (f==1)?x:-x;
}
void add(int x,int y){
    for(;x<maxn;x+=lowbit(x)) c[x]+=y; 
}
int sum(int x){
    int ans=0;
    for(;x;x-=lowbit(x)) ans+=c[x];
    return ans;
}

int main()
{
    n=read();
    for(int i=1;i<=n;i++) 
        a[i].x=read(),a[i].y=read();
    sort(a+1,a+n+1,cmp);
    int now;
    for(int i=1;i<=n;i++){
        now=sum(a[i].y+1);
        ans[now]++;
        add(a[i].y+1,1);
    }
    for(int i=0;i<n;i++) 
        printf('%d\n',ans[i]);
    return 0;
}

三维偏序概述

三维偏序
三维偏序就是在二维偏序的基础上加上一维。
给你一个长度为n的序列,每个序列都有a,b,c三种属性,让你求具有某些关系的点对(i,j)个数。一般思路是先确定一维的顺序,分治维护第二维,再结合上边的二维偏序中的树状数组维护第三维。

  1. 找到这个序列的中点 mid
  2. 将所有点对(i,j)划分为 3 类
    第一类是1<= i<=mid,1<= j<=mid
    第二类是1<=i<=mid,mid+1<=j<=n
    第三类是mid+1<=i<=n,mid+1<=j<=n
  3. 将 这个序列拆成两个序列(1,mid)和 (mid+1,n) 会发现第一类点对和第三类点对都在这两个序列之中,递归的去解决这两类点对
  4. 通过一些神奇的操作 (分治)方法来解决第二类点对

例题分析

在这里插入图片描述
输入数据

10 3
3 3 3
2 3 3
2 3 1
3 1 1
3 1 2
1 3 1
1 1 2
1 2 2
1 3 2
1 2 1

输出数据

3
1
3
0
1
0
1
0
0
1

先按x排序。分治时每次将前半边、后半边分别按y排序。虽然现在x的顺序被打乱了,但是前半边还是都小于后半边的,所以要是只计算前半边对后半边的偏序关系,是不会受到x的影响的。维护后一半的指针i,前一半的指针j,每次将i后移一位时,若y[j]<=y[i]则不断后移j,并不断将z[j]加入树状数组。然后再查询树状数组中有多少数小于等于z[i]。 最后要清空树状数组。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005;
const int maxm = 200005;
int n, m, kk;
int sum[maxm], p[maxm];
int lowbit(int x){ return x & (-x); }
void update(int x,int k){ for (int i = x; i <= kk; i+=lowbit(i)) sum[i] += k; }
int q(int x){ int ans = 0; for (int i = x; i != 0; i-=lowbit(i)) ans += sum[i]; return ans; }

struct node{
    int x, y, z, w, ans;
} a[maxn], b[maxn];
bool rulex(node x,node y)
{
    if(x.x==y.x)
    {
        if(x.y==y.y)
            return x.z < y.z;
        return x.y < y.y;
    }
    return x.x < y.x;
}
bool ruley(node x,node y)
{
    if(x.y==y.y)
        return x.z < y.z;
    return x.y < y.y;
}

void slove(int l,int r)
{
    if(l==r)
        return;
    int mid = (l + r) / 2;
    int i = l, j = mid + 1;
    slove(l, mid);
    slove(mid + 1, r);
    sort(a + l, a + mid + 1, ruley);
    sort(a + mid + 1, a + r + 1, ruley);
    while(j<=r)
    {
        while(a[j].y>=a[i].y&&i<=mid)
            update(a[i].z, a[i].w), i++;
        a[j].ans += q(a[j].z);
        j++;
    }
    for (j = l; j < i; j++)
        update(a[j].z, -a[j].w);
}

int main()
{
    scanf("%d%d", &m, &kk);
    for (int i = 1; i <= m; i++)
        scanf("%d%d%d", &b[i].x, &b[i].y, &b[i].z);
    sort(b + 1, b + 1 + m, rulex);
    int cnt = 0, n = 0;
    for (int i = 1; i <= m; i++)
    {
        cnt++;
        if(b[i].x!=b[i+1].x||b[i].y!=b[i+1].y||b[i].z!=b[i+1].z)
            a[++n] = b[i], a[n].w = cnt, cnt = 0;
    }
    slove(1, n);
    for (int i = 1; i <= n; i++)
        p[a[i].ans + a[i].w - 1] += a[i].w;
    for (int i = 0; i < m; i++)
        printf("%d\n", p[i]);

    return 0;
}

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值