COGS 2479. [HZOI 2016] 偏序 (CDQ套CDQ)

本文介绍了解决四维偏序问题的一种高效算法:通过CDQ分治策略,先进行一维排序,再处理二维,最后使用树状数组解决最后一维,实现对复杂数据结构的有效管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

 

解题思路

四维偏序问题,模仿三维偏序,第一维排序,第二维CDQ,最后剩下二元组,发现没办法处理,就继续嵌套CDQ分治。首先把二元组的左右两边分别打上不同的标记,因为统计答案时只统计左边对右边的影响,然后再进行一个CDQ解决第三维,最后用树状数组解决最后一维。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>

using namespace std;
const int MAXN = 50005;
const int LEFT = 1;
const int RIGHT = 2;
typedef long long LL;

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {f=ch=='-'?0:1;ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return f?x:-x;
}

int n;
LL ans,f[MAXN];

struct Query{
    int a,b,c,id,New;
}q[MAXN],tmp[MAXN],tmp_[MAXN];

void update(int x,int k){
    for(;x<=n;x+=x&-x) f[x]+=k;
}

LL query(int x){
    LL ret=0;
    for(;x;x-=x&-x) ret+=f[x];
    return ret;
}

void Clear(int x){
    for(;x<=n;x+=x&-x) f[x]=0;
}

void CDQ(int l,int r){
    if(l==r) return;
    int mid=l+r>>1;CDQ(l,mid);CDQ(mid+1,r);
    int L=l,R=mid+1,o=l;
    while(L<=mid && R<=r){
        if(tmp[L].b<tmp[R].b){
            if(tmp[L].New==LEFT) update(tmp[L].c,1);
            tmp_[o++]=tmp[L++];
        }
        else{
            if(tmp[R].New==RIGHT) ans+=query(tmp[R].c);
            tmp_[o++]=tmp[R++];
        }
    }
    while(L<=mid) tmp_[o++]=tmp[L++];
    while(R<=r) {
        if(tmp[R].New==RIGHT) ans+=query(tmp[R].c);
        tmp_[o++]=tmp[R++];
    }
//    memset(f,0,sizeof(f));
    for(register int i=l;i<=mid;i++) if(tmp[i].New==LEFT) Clear(tmp[i].c);
    for(register int i=l;i<=r;i++) tmp[i]=tmp_[i];
}

void cdq(int l,int r){
    if(l==r) return;
    int mid=l+r>>1;cdq(l,mid);cdq(mid+1,r);
    int L=l,R=mid+1,o=l;
    while(L<=mid && R<=r){
        if(q[L].a<q[R].a) {
            q[L].New=LEFT;
            tmp[o++]=q[L++];
        }
        else {
            q[R].New=RIGHT;
            tmp[o++]=q[R++];
        }
    }
    while(L<=mid) {q[L].New=LEFT;tmp[o++]=q[L++];}
    while(R<=r)  {q[R].New=RIGHT;tmp[o++]=q[R++];}
    for(register int i=l;i<=r;i++) q[i]=tmp[i];
    CDQ(l,r);
}

int main(){
//    freopen("data.txt","r",stdin);
//    freopen("wrong.txt","w",stdout);
    freopen("partial_order.in","r",stdin);
    freopen("partial_order.out","w",stdout);
    n=rd();
    for(int i=1;i<=n;i++) q[i].a=rd(),q[i].id=i;
    for(int i=1;i<=n;i++) q[i].b=rd();
    for(int i=1;i<=n;i++) q[i].c=rd();
    cdq(1,n);cout<<ans<<endl;
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/sdfzsyq/p/9700580.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值