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的元素有多少个。最后进行步骤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;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-11 03:38:30

HDU 5126(stars)四维偏序,cdq分治的相关文章

HDU 5126 stars cdq分治+树状数组

题目链接:点击打开链接 题意: T个case n个操作 1. (x,y,z) 在三维平面的点上增加1 2.询问区间范围内的权值和. 思路: cdq分治套cdq分治,然后套树状数组即可.. #include <stdio.h> #include <iostream> #include <algorithm> #include <sstream> #include <stdlib.h> #include <string.h> #inclu

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

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分治.这样可以干掉一维,保证随

HDU 3507 Print Article(CDQ分治+分治DP)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=3507 [题目大意] 将长度为n的数列分段,最小化每段和的平方和. [题解] 根据题目很容易得到dp[j]=min(dp[k]+(s[j]-s[k])2),因为是从前往后转移,且决策单调,因此在CDQ分治的同时进行分治DP即可. [代码] #include <cstdio> typedef long long LL; const int N=500005; int n,M,t; LL f[N],

HDU 5730 Shell Necklace(CDQ分治+FFT)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5730 [题目大意] 给出一个数组w,表示不同长度的字段的权值,比如w[3]=5表示如果字段长度为3,则其权值为5,现在有长度为n的字段,求通过不同拆分得到的字段权值乘积和. [题解] 记DP[i]表示长度为i时候的答案,DP[i]=sum_{j=0}^{i-1}DP[j]w[i-j],发现是一个卷积的式子,因此运算过程可以用FFT优化,但是由于在计算过程中DP[j]是未知值,顺次计算复杂度是O(

HDU 5324 Boring Class【cdq分治】

这就是一个三维排序的问题,一维递减,两维递增,这样的问题用裸的CDQ分治恰好能够解决. 如同HDU 4742(三维排序,一个三维都是递增的) 由于最小字典序比較麻烦,所以要从后面往前面做分治.每一个点的dp值表示以这个点为起点.最长能够延伸多长. 本来是想依照Li排序,可是这样做在cdq的时候实在是太难以处理了.所以就依照idx排序.也就是不须要排序. 然后依照Ri排序,对于左边,保证右边的每一个R值大于左边的值.并用树状数组维护Li(由于Li须要递减.所以树状数组恰好能够维护右边的Li小于左边

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 5730 Shell Necklace fft+cdq分治

题目链接 dp[n] = sigma(a[i]*dp[n-i]), 给出a1.....an, 求dp[n]. n为1e5. 这个式子的形式显然是一个卷积, 所以可以用fft来优化一下, 但是这样也是会超时的. 所以可以用cdq分治来优化. cdq分治就是处理(l, mid)的时候, 将dp[l]...dp[mid]对dp[mid+1]...dp[r]做的贡献都算出来. #include <bits/stdc++.h> using namespace std; #define pb(x) pus

HDU - 5126 stars (CDQ分治)

题目大意:一共有Q(1<=Q<=50000)组操作,操作分为两种: 1.在x,y,z处添加一颗星星 2.询问以(x1,y1,z1)与(x2,y2,z2)为左上和右下顶点的矩形之间的星星数 所有坐标取值范围均为[1,1e9] 思路:由于坐标取值范围太大(即使离散化后也很大),3维的数组肯定开不下,所以只能另辟蹊径. 解法一(两重CDQ+树状数组,需将z坐标离散化): 1)将每个查询操作拆分为8个(容斥),将所有操作放入一个数组qr中 2)将所有操作按时间排序(其实不用,因为读入的顺序就是按时间排