HDU5126 stars(4维偏序->cdq套cdq+树状数组)

stars

题目大意:
在一个三维空间当中,每次进行一个操作,添加一个点或者统计空间中的某一个长方体范围内的所有点

三维空间中我们用两个点即可确定一个长方体。

首先效仿平面二维数点的方法,根据容斥原理可以把询问拆分成8个以原点 O ( 0 , 0 , 0 ) O(0,0,0) O(0,0,0)为一个顶点长方体的内部点的数量,像这样的长方体可以用一个坐标 ( x , y , z ) (x,y,z) (x,y,z)表示

假设当前有一个点在 t 0 t_0 t0时刻插入位置为 ( x 0 , y 0 , z 0 ) (x_0,y_0,z_0) (x0,y0,z0),如果这个点在 t t t时刻一个以原点为一个端点的长方体 ( x , y , z ) (x,y,z) (x,y,z)内部条件: t 0 < t , x 0 ≤ x , y 0 ≤ y , z 0 ≤ z t_0<t,x_0\leq x,y_0\leq y,z_0\leq z t0<t,x0x,y0y,z0z
由上面条件不难看出是一个4维偏序问题。
对于3维偏序三维偏序(陌上花开)只需要用cdq分治+树状数组即可解决,当然同样可以用cdq分治套cdq分治解决(强烈建议写此题前,用cdq套cdq写一下三维偏序(陌上花开)

4维偏序只需要 cdq套cdq+树状数组

注意需要对z进行离散化

#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
constexpr int N=50010;
struct node
{
    int op;
    int x,y,z;
    int sign,id;
    int part;
}q[8*N];
int n,nn,cnt;
int b[2*N],c[N];
int ans[N];
int fw[2*N];
int lowbit(int x){return x&-x;}
void update(int k,int x){for(;k<=nn;k+=lowbit(k)) fw[k]+=x;}
int query(int k){int res=0;for(;k;k-=lowbit(k)) res+=fw[k];return res;}

void cdq(int l,int r)
{
    if(l>=r) return;
    int mid=l+r>>1;
    cdq(l,mid),cdq(mid+1,r);
    int i=l;
    for(int j=mid+1;j<=r;j++)
    {
        while(i<=mid&&q[i].y<=q[j].y)
        {
            if(q[i].op==0&&q[i].part==0) update(q[i].z,1);
            i++;
        }
        if(q[j].op==1&&q[j].part==1) ans[q[j].id]+=q[j].sign*query(q[j].z);
    }
    while(i>l) 
    {
        i--;
        if(q[i].op==0&&q[i].part==0) update(q[i].z,-1);
    }
    inplace_merge(q+l,q+mid+1,q+r+1,[](const node&a,const node&b){return a.y<b.y;});
    
}
void solve(int l,int r)
{
    if(l>=r) return;
    int mid=l+r>>1;
    solve(l,mid),solve(mid+1,r);
    
    for(int i=l;i<=mid;i++) q[i].part=0;
    for(int i=mid+1;i<=r;i++) q[i].part=1;
    stable_sort(q+l,q+r+1,[](const node&a,const node&b){return a.x<b.x;});
    cdq(l,r);
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n;
        cnt=nn=0;
        for(int i=1;i<=n;i++) ans[i]=0,c[i]=0;
        for(int i=1;i<=n;i++)
        {
            int op;
            cin>>op;
            if(op==1)
            {
                int x,y,z;
                cin>>x>>y>>z;
                q[++cnt]={0,x,y,z};
                b[++nn]=z;
            }
            else
            {
                c[i]=1;
                int x1,y1,z1,x2,y2,z2;
                cin>>x1>>y1>>z1>>x2>>y2>>z2;
                q[++cnt]={1,x2,y2,z2,1,i};
                q[++cnt]={1,x1-1,y2,z2,-1,i};
                q[++cnt]={1,x2,y1-1,z2,-1,i};
                q[++cnt]={1,x2,y2,z1-1,-1,i};
                q[++cnt]={1,x1-1,y1-1,z2,1,i};
                q[++cnt]={1,x1-1,y2,z1-1,1,i};
                q[++cnt]={1,x2,y1-1,z1-1,1,i};
                q[++cnt]={1,x1-1,y1-1,z1-1,-1,i};
                b[++nn]=z1-1;
                b[++nn]=z2;
            }
        }
        sort(b+1,b+1+nn);
        nn=unique(b+1,b+1+nn)-b-1;
        for(int i=1;i<=cnt;i++) q[i].z=lower_bound(b+1,b+1+nn,q[i].z)-b;
        solve(1,cnt);
        for(int i=1;i<=n;i++)
            if(c[i]) cout<<ans[i]<<'\n';
    }
    return 0;
}

感觉网上此题的题解都好久远,难道没人水水这题吗???
1A非常开心,要加油哦~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值