HDU 5126(stars)四维偏序,cdq分治

题意:给两种操作,进行5万次。操作一:加入一个三维序偶(a,b,c)到集合S里;第二种操作,给两个三维序偶(a1,b1,c1)和(a2,b2,c2),问当前S里有多少个序偶(a,b,c)满足a1<=a<=a2, b1<=b<=b2, c1<=c<=c2。题目保证了a1<=a2,b1<=b2,c1<=c2。所有数在[1,1e9]内
链接:http://acm.hdu.edu.cn/showproblem.php?pid=5126

解法:将操作编号也加入到序偶中,构成4维序偶,问题转换为四维偏序问题。第一次写,只写了一种写法,即cdq套cdq套树状数组的方法,以后会再尝试cdq套树套树等方法,再更新此博客。下面耐心地讲一下第一种方法。

我们定义四维序偶(i,a,b,c),a,b,c为输入的变量,i为当前操作编号

预处理,将第4维(c)离散化,再进行一系列初始化操作(略);给四维序偶增加一维变量k(但不增加复杂度),即(i,a,b,c,k),k=0表示这个序偶是第一种操作的序偶,也就是插入的序偶;k=-1或1时,表示这个序偶时第二种操作的序偶,也就是查询的序偶。
第一种操作,序偶为(i,a,b,c,0);
第二种操作,序偶为(i,a1,b1,c1,k1),(i,a2,b2,c2,k2);将这两个序偶拆成8个序偶,即为(i,a2,b2,c2,+1), (i,a1-1,b2,c2,-1), (i,a2,b1-1,c2,-1), (i,a2,b2,c1-1,-1), (i,a2,b1-1,c1-1,+1), (i,a1-1,b2,c1-1,+1), (i,a1-1,b1-1,c2,+1), (i,a1-1,b1-1,c1-1,-1);细心可以发现,这8个序偶实际上是在做容斥原理,为了统计操作二而做的。

一、先将 i 从小到大排序(其实按照输入,已经排序好了),这是第一维;设n个序偶(i,a,b,c,k)存储在p1中

二、对p1归并,归并[1, n]
1、归并[l, r]:若 l < r,设mid = (l + r) >> 1,递归进行步骤一,归并[l, mid],再进行步骤2;否则,终止。
2、在[l, mid]中将k=0的序偶依次加入到p2中,再在[mid+1, r]中将k=-1或k=1的序偶依次加入到p2中,设此时p2有n2个序偶;对p2进行排序(先以x优先,再以i优先),然后进行操作三,对p2归并,归并[1,n2]。最后进行步骤3。
3、进行步骤一,归并[mid+1, r]。

三、对p2归并,归并[1, n2]
1、归并[l, r]:若l < r,设mid = (l + r) >> 1,递归进行步骤一,归并[l, mid],再进行步骤2;否则,终止。
2、在[l, mid]中将k=0的序偶依次加入到s1中,设有ns1个;在[mid+1, r]中将k=-1或k=1的序偶依次加入到s2中,设有ns2个;对s1和s2分别进行排序(按y优先),用二指针法,保证在更新每个s2的序偶之前,s1中每个y值不大于当前序偶的y值的序偶都已加入树状数组里。树状数组维护前缀和。设当前s2的序偶为(i,a,b,c,k),则ans[i] += k * query(c),query(c)表示查询树状数组,不大于c的元素有多少个。在s2所有序偶都更新完后,清空树状数组中s1序偶更新的值。最后进行步骤3。
3、进行步骤一,归并[mid+1, r]。

小结:cdq分治,每降一维只需用log的复杂度,反复品味cdq分治,和多做cdq分治的题目才是理解cdq的王道。

没有配图,解释得并不清楚,主要还是看代码(选择g++后提交)

//Hello. I'm Peter.
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<bitset>
#include<set>
#include<map>
using namespace std;
#define peter cout<<"i am peter"<<endl
#define fuck(x) cerr << #x << " <- " << x << endl
typedef long long ll;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int Q;
struct Pair{
    int x, y, z, k, id;
    Pair(){}
    Pair(int x1,int y1,int z1,int k1,int id1){
        x = x1, y = y1, z = z1, k = k1, id = id1;
    }
};
bool cmpxid(const Pair a, const Pair b){
    if(a.x != b.x) return a.x < b.x;
    else return a.id < b.id;
}
bool cmpy(const Pair a, const Pair b){
    return a.y < b.y;
}
#define N 50010
Pair p1[N*8],s1[N*8],s2[N*8],p2[N*8];
int np1;

int li[N*2],nli,ans[N];

namespace bit{
    int bit[N*2];
    void clear(){
        for(int i = 1; i <= nli; i++) bit[i] = 0;
    }
    void update(int x,int val){
        for(int i = x; i <= nli; i += i&-i){
            bit[i] += val;
        }
    }
    int query(int x){
        int ans = 0;
        for(int i = x; i > 0; i -= i&-i){
            ans += bit[i];
        }
        return ans;
    }
    void clear(int x){
        for(int i = x; i <= nli; i += i&-i){
            bit[i] = 0;
        }
    }
};

void cdq2(int l,int r){
    if(r <= l) return;
    int mid = (l + r) >> 1;
    cdq2(l, mid);

    int ns1 = 0, ns2 = 0;
    for(int i = l; i <= mid; i++) if(!p2[i].k) s1[++ns1] = p2[i];
    for(int i = mid + 1; i <= r; i++) if(p2[i].k) s2[++ns2] = p2[i];
    sort(s1 + 1, s1 + 1 + ns1, cmpy);
    sort(s2 + 1, s2 + 1 + ns2, cmpy);
    int t1 = 1;
    for(int t2 = 1; t2 <= ns2; t2++){
        while(t1 <= ns1 && s1[t1].y <= s2[t2].y) bit::update(s1[t1].z, +1), t1++;
        ans[s2[t2].id] += s2[t2].k * bit::query(s2[t2].z);
    }
    for(int i = 1; i < t1; i++) bit::clear(s1[i].z);

    cdq2(mid + 1, r);
}

void cdq1(int l,int r){
    if(r <= l) return;
    int mid = (l + r) >> 1;
    cdq1(l, mid);

    int np2 = 0;
    for(int i = l; i <= mid; i++) if(!p1[i].k) p2[++np2] = p1[i];
    for(int i = mid + 1; i <= r; i++) if(p1[i].k) p2[++np2] = p1[i];
    sort(p2 + 1, p2 + 1 + np2, cmpxid);
    cdq2(1, np2);

    cdq1(mid + 1, r);
}

bool isque[N];
int main(){
    int T = read();
    for(int kase = 1; kase <= T; kase++){
        Q = read();
        np1 = nli = 0;
        for(int i = 1; i <= Q; i++){
            int A = read();
            isque[i] = A == 2? 1: 0;

            if(A == 1){
                int x, y, z;
                x = read(), y = read(), z = read();
                p1[++np1] = Pair(x, y, z, 0, i);
                li[++nli] = z;
            }
            else{
                int x1,y1,z1,x2,y2,z2;
                x1=read(),y1=read(),z1=read(),x2=read(),y2=read(),z2=read();
                p1[++np1] = Pair(x2, y2, z2, 1, i);
                p1[++np1] = Pair(x1 - 1, y2, z2, -1, i);
                p1[++np1] = Pair(x2, y1 - 1, z2, -1, i);
                p1[++np1] = Pair(x2, y2, z1 - 1, -1, i);
                p1[++np1] = Pair(x1 - 1, y1 - 1, z2, 1, i);
                p1[++np1] = Pair(x2, y1 - 1, z1 - 1, 1, i);
                p1[++np1] = Pair(x1 - 1, y2, z1 - 1, 1, i);
                p1[++np1] = Pair(x1 - 1, y1 - 1, z1 - 1, -1, i);

                li[++nli] = z2;
                li[++nli] = z1 - 1;
            }
        }

        sort(li + 1, li + 1 + nli);
        nli = (int)(unique(li + 1, li + 1 + nli) - (li + 1));
        for(int i = 1; i <= np1; i++) p1[i].z = (int)(lower_bound(li + 1, li + 1 + nli, p1[i].z) - li);

        bit::clear();
        for(int i = 1; i <= Q; i++) ans[i] = 0;
        cdq1(1, np1);

        for(int i = 1; i <= Q; i++) if(isque[i]) printf("%d\n",ans[i]);
    }
    return 0;
}

你可能感兴趣的:(cdq分治,四维偏序)