CDQ分治&&整体二分

以下资料参考自 Owen_codeisking大佬的博客

一、(CDQ)分治

首先,建议各位小盆友先前置一下树状数组和分治的知识

1.二维偏序

题目:【模板】二维偏序&&HDU1541 Stars

二维偏序问题:给你(n)个点,以及这(n)个点坐标(X_{i})(Y_{i}),令(F{i}=X{j}le X{i}且Y{j}le Y{i})的点的个数,求(F{i})

这一看,当然可以用暴力解法,最暴力的可以达到(O(n^2))的时间复杂度,但只能在(nle5000)时用

但如果(10000le n)呢?

或许您有(n^2)过百万的经历(那是因为您是大佬),但下面我们就介绍一种(O(nlogn))的算法:(CDQ)分治最基础的运用

我们把这个问题看成一张图(偷来的)
在这里插入图片描述那么,图中被圈起来的点就是对于矩形右上角满足条件的点

首先,我们先将点的纵坐标(y{i})从小到大排序(排序横坐标还是纵坐标看心情(随便)),这个用一个(sort)就可以实现

接着,我们保证了(y{i})的从小到大后,就开始对横坐标进行排序

这个排序操作可以用树状数组实现

int c[N];
void add(int x,int y)
{
	for(;x<=N;x+=lowbit(x))c[x]+=y;
}
int sum(int x)
{
	int ans=0;
	for(;x>0;x-=lowbit(x))ans+=c[x];
	return ans;
}

这就是树状数组模板,不多讲

#include<bits/stdc++.h>
using namespace std;
# define lowbit(x) ((x)&(-(x))) 
# define int long long 
const int N=100010;
int n;
struct edge
{
    int a,b;
}p[N];
int c[N];
bool cmp(edge a,edge b)
{
    if(a.a==b.a)return a.b<b.b;
    return a.a<b.a;
}
int sum(int x)//从大往小搜索
{
    int ans=0;
    for(;x;x-=lowbit(x))ans+=c[x];
    return ans;
}
void add(int x,int y)
{
    for(;x<N;x+=lowbit(x))c[x]+=y;
}
signed main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)scanf("%lld %lld",&p[i].a,&p[i].b);
    sort(p+1,p+n+1,cmp);
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        int now=sum(p[i].b+1);//这就是f[i]的值
        ans+=now;
        add(p[i].b+1,1);
    }
    printf("%lld",ans);
    return 0;
}

2.三维偏序

题目:P3810 【模板】三维偏序(陌上花开)

这个就是在二维偏序上多加一维,从原来条件的(X{j}le X{i}且Y{j}le Y{i})增加到(X{j}le X{i}且Y{j}le Y{i}且Z{j}le Z{i})

这时,我们还是先按横坐标排序,满足第一维条件

然后,我们用归并排序满足第二维条件

再用树状数组,满足第三维条件

这里我们就来详细看看归并排序

我们在归并时,考虑对于区间([l,mid])对区间([mid+1,r])贡献,因为我们已经通过排序满足了第一维条件,所以不论怎么打散,([l,mid])区间的所有数都是小于等于([mid+1,r])的数的

我们首先设一个结构体

struct edge
{
    int a,b,c,re,ans;//re表示与e[i]重复的点的个数,ans表示对于i满足条件的节点个数
}e[N],t[N];
归并,顾名思义,递归排序合并,因此,归并需要靠递归实现。
	if(l==r)return ;
    int mid=(l+r)>>1;
    cdq(l,mid);cdq(mid+1,r);

相信以上代码很好理解

重难点:敲黑板

在这里插入图片描述
接着,我们需要分别遍历区间([l,mid])和区间([mid+1,r])中的数并且相互比较
我们令区间([l,mid])中遍历到的点为(p),区间([mid+1,r])中的为(q),在设一个结构体(t)存储更改过顺序的点。

  1. 如果(p)的第二维小于等于(q)中的第二维
    那么第一维和第二维都满足了,则直接树状数组满足第三维,更新(t),且继续遍历下一个(p)
  2. 如果不满足上一个条件,则将(ans)更新,加上对于(q)满足条件的节点的个数,并且更新(t),且继续遍历下一个(q)
	while(p<=mid&&q<=r)
    {
        if(e[p].b<=e[q].b)add(e[p].c,e[p].re),t[tot++]=e[p++];
        else e[q].ans+=sum(e[q].c),t[tot++]=e[q++];
    } 

(while)结束后,担心还有点没有遍历到,于是要从当前的(p)遍历到(mid),从(mid+1)(q),操作都与上面代码相同

 	while(p<=mid)add(e[p].c,e[p].re),t[tot++]=e[p++];
    while(q<=r)e[q].ans+=sum(e[q].c),t[tot++]=e[q++];

最后还原树状数组,因为我们只是为了更新(t)数组,为了之后的操作实现,需要还原

并将(t)复制给当前结构体

	for(int i=l;i<=mid;i++)add(e[i].c,-e[i].re);
    for(int i=l;i<=r;i++)e[i]=t[i];

整代码(我知道你们只看这个)

#include<bits/stdc++.h>
#define lowbit(x) ((x)&(-(x)))
#define N 100010
using namespace std;
int n,m;
int output[N];
int c[N];
struct edge
{
    int a,b,c,re,ans;
}e[N],t[N];
bool cmp(edge a,edge b)
{
    if(a.a!=b.a)return a.a<b.a;
    if(a.b!=b.b)return a.b<b.b;
    return a.c<b.c;
}
void add(int x,int y)
{
    for(;x<=m;x+=lowbit(x))c[x]+=y;
}
int sum(int x)
{
    int ans=0;
    for(;x;x-=lowbit(x))ans+=c[x];
    return ans;
}
void cdq(int l,int r)
{
    if(l==r)return ;
    int mid=(l+r)>>1;
    cdq(l,mid);cdq(mid+1,r);
    int p=l,q=mid+1,tot=l;
    while(p<=mid&&q<=r)
    {
        if(e[p].b<=e[q].b)add(e[p].c,e[p].re),t[tot++]=e[p++];
        else e[q].ans+=sum(e[q].c),t[tot++]=e[q++];
    } 
    while(p<=mid)add(e[p].c,e[p].re),t[tot++]=e[p++];
    while(q<=r)e[q].ans+=sum(e[q].c),t[tot++]=e[q++];
    for(int i=l;i<=mid;i++)add(e[i].c,-e[i].re);
    for(int i=l;i<=r;i++)e[i]=t[i];
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d %d %d",&e[i].a,&e[i].b,&e[i].c);
        e[i].re=1;
    }
    sort(e+1,e+n+1,cmp);
    int cnt=1;
    for(int i=2;i<=n;i++)
    {
        if(e[cnt].a==e[i].a&&e[cnt].b==e[i].b&&e[cnt].c==e[i].c)e[cnt].re++;
        else e[++cnt]=e[i];
    }
    cdq(1,cnt);
    for(int i=1;i<=cnt;i++)output[e[i].ans+e[i].re-1]+=e[i].re;
    for(int i=0;i<n;i++)printf("%d
",output[i]);
    return 0;
}

tips:因为在P3810 【模板】三维偏序(陌上花开),有可能出现重复的点,所以需要判重

  1. 四维偏序

太变态了,蒟蒻暂时还不会,想了解的可以看看博客顶的参考博客

二、整体二分

原文地址:https://www.cnblogs.com/ShuraEye/p/11354541.html