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;
}
View Code

 

posted @ 2016-08-15 13:46  wust_ouyangli  阅读(437)  评论(0编辑  收藏  举报