Hdu5126-stars(两次CDQ分治)

题意: 简化就是有两种操作,一种是插入(x,y,z)这个坐标,第二种是查询(x1,y1,z1)到(x2,y2,z2)(x1<=x2,y1<=y2,z1<=z2)的长方体包含多少个点。

解析: 将查询分成8个点,离线做,离散化z值,
两次CDQ,第一次归并排x值,第二次归并排y值,z值用bit树维护更新
查询。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
using namespace std;
const int maxn=9*50000;
int N,Q,ans[maxn];
struct node
{
    int x,y,z,id,s;//(x,y,z)三维坐标,id编号,s代表状态
    node(int x=0,int y=0,int z=0,int id=0,int s=0)
    :x(x),y(y),z(z),id(id),s(s){}
}A[maxn],B[maxn],C[maxn];//A保存原数组,B保存中间过程,C用于归并排序临时数组
bool cmpid(const node& a,const node& b){ return a.id<b.id; } //排id
bool cmpz(const node& a,const node& b){ return a.z<b.z; } //排z值
int tree[maxn];//bit树实现部分
int lowbit(int x){ return x&(-x); }
void Add(int i,int d){ for(;i<maxn;i+=lowbit(i)) tree[i]+=d; }
int Sum(int i)
{
    int ret=0;
    for(;i>0;i-=lowbit(i)) ret+=tree[i];
    return ret;
}
void CDQ2(int l,int r) //归并排y值,对z值查询更新
{
    if(l>=r) return;
    int mid=(l+r)/2;
    CDQ2(l,mid);
    CDQ2(mid+1,r);
    int ls=l,rs=mid+1;
    while(rs<=r)
    {
        while(ls<=mid&&B[ls].y<=B[rs].y) //选取y较小的
        {
            if(B[ls].s==0) Add(B[ls].z,1); //如果是0则需要插入bit树
            ls++;
        }
        if(B[rs].s!=0) ans[B[rs].id]+=Sum(B[rs].z)*B[rs].s; //右边的是查询
        rs++;
    }
    while(ls>l) //恢复
    {
        ls--;
        if(B[ls].s==0) Add(B[ls].z,-1);
    }
    ls=l,rs=mid+1;
    for(int i=l;i<=r;i++) //归并排序过程,排y值
    {
        if((ls<=mid&&B[ls].y<=B[rs].y)||rs>r) C[i]=B[ls++];
        else C[i]=B[rs++];
    }
    for(int i=l;i<=r;i++) B[i]=C[i];
}
void CDQ1(int l,int r)//归并排x值,选取的过程是时序
{
    if(l==r) return;
    int mid=(l+r)/2;
    CDQ1(l,mid);
    CDQ1(mid+1,r);
    int ls=l,rs=mid+1,k=0;
    while(rs<=r)
    {
        while(ls<=mid&&A[ls].s!=0) ls++; //前一半而且是查询不管
        while(rs<=r&&A[rs].s==0) rs++; //后一半而且是插入不管
        if(rs>r) break; //这个地方可能难理解,仔细 想一下如果右边已经没有查询的操作了,
                        //剩下的左边的时序绝对比最后一次加到B中的查询的操作时序要大
        if((A[ls].x<=A[rs].x&&ls<=mid)||rs>r) B[k++]=A[ls++];
        else B[k++]=A[rs++];
    }
    if(k>0) CDQ2(0,k-1);  //再一次CDQ
    ls=l,rs=mid+1;
    for(int i=l;i<=r;i++) //归并排序
    {
        if((ls<=mid&&A[ls].x<=A[rs].x)||rs>r) C[i]=A[ls++];
        else C[i]=A[rs++];
    }
    for(int i=l;i<=r;i++) A[i]=C[i];
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        N=0;
        int type,x1,y1,z1,x2,y2,z2;
        scanf("%d",&Q);
        while(Q--)
        {
            scanf("%d",&type);
            if(type==1)
            {
                scanf("%d%d%d",&x1,&y1,&z1);
                A[N++]=node(x1,y1,z1,N,0); //要插入的点
            }
            else
            {
                scanf("%d%d%d",&x1,&y1,&z1);
                scanf("%d%d%d",&x2,&y2,&z2);
                A[N++]=node(x2,y2,z2,N,1);   //分成8个查询的点
                A[N++]=node(x2,y1-1,z2,N,-1);
                A[N++]=node(x1-1,y2,z2,N,-1);
                A[N++]=node(x2,y2,z1-1,N,-1);
                A[N++]=node(x1-1,y1-1,z2,N,1);
                A[N++]=node(x1-1,y2,z1-1,N,1);
                A[N++]=node(x2,y1-1,z1-1,N,1);
                A[N++]=node(x1-1,y1-1,z1-1,N,-1);
            }
        }
        memset(ans,0,sizeof(ans));
        sort(A,A+N,cmpz); //按照z值排序
        int ka=1;
        A[N].z=-1;
        for(int i=0;i<N;i++)
            if(A[i].z!=A[i+1].z) A[i].z=ka++; //离散化
            else A[i].z=ka;
        sort(A,A+N,cmpid); //按照时序排回来
        memset(tree,0,sizeof(tree));
        CDQ1(0,N-1);
        sort(A,A+N,cmpid); //再排回来
        for(int i=0;i<N;i++) //输出答案
        {
            if(A[i].s==0) continue;
            int sum=0;
            for(int j=0;j<8;j++) sum+=ans[i+j];
            printf("%d\n",sum);
            i+=7;
        }
    }
    return 0;
}

时间: 2024-11-06 15:59:07

Hdu5126-stars(两次CDQ分治)的相关文章

【Luogu1393】动态逆序对(CDQ分治)

[Luogu1393]动态逆序对(CDQ分治) 题面 题目描述 对于给定的一段正整数序列,我们定义它的逆序对的个数为序列中ai>aj且i < j的有序对(i,j)的个数.你需要计算出一个序列的逆序对组数及其删去其中的某个数的逆序对组数. 输入输出格式 输入格式: 第一行,两个数n,m,表示序列中有n个数,要删去m个数 第二行n个数,表示给定的序列. 第三行m个数,第i个数di表示要删去原序列中的第di个数. 输出格式: 一行m+1个数.第一个数表示给定序列的逆序对组数,第i+1个数表示删去第d

luogu P3157 [CQOI2011]动态逆序对(CDQ分治)

题目描述 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. 输入输出格式 输入格式: 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. 输出格式: 输出包含m行,依次为删除每个元素之前,逆序对的个数. 题解 我们发现一个数的贡献,就是就是t'<

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 解法:将操作编号也加入到

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)将所有操作按时间排序(其实不用,因为读入的顺序就是按时间排

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

CDQ分治与整体二分小结

前言 这是一波强行总结. 下面是一波瞎比比. 这几天做了几道CDQ/整体二分,感觉自己做题速度好慢啊. 很多很显然的东西都看不出来 分治分不出来 打不出来 调不对 上午下午晚上的效率完全不一样啊. 完蛋.jpg 绝望.jpg. 关于CDQ分治 CDQ分治,求的是三维偏序问题都知道的. 求法呢,就是在分治外面先把一维变成有序 然后分治下去,左边(l,mid)关于右边(mid+1,r)就不存在某一维的逆序了,所以只有两维偏序了. 这个时候来一波"树状数组求逆序对"的操作搞一下二维偏序 就可

BZOJ 3262: 陌上花开 [CDQ分治 三维偏序]

Description 有n朵花,每朵花有三个属性:花形(s).颜色(c).气味(m),又三个整数表示.现要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量.定义一朵花A比另一朵花B要美丽,当且仅当Sa>=Sb,Ca>=Cb,Ma>=Mb.显然,两朵花可能有同样的属性.需要统计出评出每个等级的花的数量. Input 第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,000 ), 分别表示花的数量和最大属性值. 以下N行,每

ACdream1157 Segments(CDQ分治 + 线段树)

题目这么说的: 进行如下3种类型操作:1)D L R(1 <= L <= R <= 1000000000) 增加一条线段[L,R]2)C i (1-base) 删除第i条增加的线段,保证每条插入线段最多插入一次,且这次删除操作一定合法3) Q L R(1 <= L <= R <= 1000000000) 查询目前存在的线段中有多少条线段完全包含[L,R]这个线段,线段X被线段Y完全包含即LY <= LX <= RX <= RY) 初学CDQ分治是看了B

BZOJ 2726: [SDOI2012]任务安排( dp + cdq分治 )

考虑每批任务对后面任务都有贡献, dp(i) = min( dp(j) + F(i) * (T(i) - T(j) + S) ) (i < j <= N)  F, T均为后缀和. 与j有关的量只有t = dp(j) - F(i) * T(j) , 我们要最小化它. dp(j)->y, T(j)->x, 那么y = F(i) * x + t, 就是给一些点和一个斜率...然后最小化截距, 显然维护下凸包就可以了. 然后因为无比坑爹的出题人....时间可以为负数, 所以要用平衡树维护(