题解 三维偏序(陌上花开)+黑科技食用

Sol

前言

在看题目之前,先给大家安利一个好博客,博主就是用过这篇博客和巨佬YCB的帮助下学会的
想学会CDQ就戳我

题目大意

n n 个元素,第i个元素有三个属性 ai a i bi b i ci c i
f(i) f ( i ) 为表示满足

ajai,bjbi,cjci a j ≤ a i , b j ≤ b i , c j ≤ c i
j j 的数量
对于 d[0,n),求 f(i) f ( i ) = d d 的数量

题解

想必大家都是为了学习CDQ分治才来刷这道题目的(会Treap的大佬请走开)
首先我们考虑二维偏序,只需将第一维例如a排序,然后在确保 a a 有序的前提下利用各种算法算顺序对bj,bi,例如Splay,归并,线段树等等
然后至于三维,四维甚至五维六维的,其实就是不断的往上套数据结构(或者继续CDQ分支)罢了,太高序的其实只是套板子的过程了( 貌似低维也是套板子的过程)
本题我采用的是 归并套树状数组(貌似楼上没有这种算法)
先将一维排序,然后再用归并排序,值得注意的是
在左区间A,和右区间B中,区间A的a一定小于B,但是A内的a再归并后是否还有序就不一定了!!!
再归并左区间过程中只修改(因为只算对右区间的贡献!!!其内部的贡献会在下层递归中找到),在右区间过程中才查询!!!
然后我的代码用的是 左闭右闭的区间,这样更加方便

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define mid ((l+r)>>1)
#define lowbit(x) (x&(-x))
#define ll long long
const int _=200100;
inline int read()
{
    char ch='!';int z=1,num=0;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')z=-1,ch=getchar();
    while(ch<='9'&&ch>='0')num=(num<<3)+(num<<1)+ch-'0',ch=getchar();
    return z*num;
}
int N,K,top,bel[_];
ll ans[_],g[_],c[_],f[_];
struct hand
{
    int a,b,c,sz,id;
    bool operator < (const hand &A)const
        {
            if(b!=A.b)return b<A.b;
            if(c!=A.c)return c<A.c;
            return id<A.id;
        }
}t[_],tmp[_],q[_];
bool cmp(hand A,hand B)
{
    if(A.a!=B.a)return A.a<B.a;
    if(A.b!=B.b)return A.b<B.b;
    if(A.c!=B.c)return A.c<B.c;
    return A.id<B.id;
}
void Update(int x,int d)
{
    while(x<=K)
    {
        c[x]+=d;
        x+=lowbit(x);
    }
}
void Clear(int x,int d)
{
    while(x<=K)
    {
        c[x]=d;
        x+=lowbit(x);
    }
}
int Query(int x)
{
    int sum=0;
    while(x)
    {
        sum+=c[x];
        x-=lowbit(x);
    }
    return sum;
}
void CDQ(int l,int r)
{
    if(l==r)return;
    CDQ(l,mid);CDQ(mid+1,r);
    int p=l,q=mid+1,o=l-1;
    while(p<=mid&&q<=r)
    {
        if(t[p]<t[q])
            Update(t[p].c,t[p].sz),tmp[++o]=t[p++];
        else
            g[t[q].id]+=Query(t[q].c),tmp[++o]=t[q++];
    }
    while(p<=mid)tmp[++o]=t[p++];
    while(q<=r)
        g[t[q].id]+=Query(t[q].c),tmp[++o]=t[q++];
    for(int i=l;i<=r;++i)
        Clear(tmp[i].c,0),t[i]=tmp[i];
}
bool pd(int i,int now)
{
    if(!now)return 0;
    else return (q[i].a==t[now].a&&q[i].b==t[now].b&&q[i].c==t[now].c);
}
int main()
{
    N=read();K=read();
    for(int i=1;i<=N;++i)
        q[i].a=read(),q[i].b=read(),q[i].c=read(),q[i].sz=1,q[i].id=i;
    sort(q+1,q+1+N,cmp);
    top=0;
    for(int i=1;i<=N;++i)
    {
        if(!pd(i,top))
            t[++top]=q[i],t[top].id=top;
        else t[top].sz++;
        bel[i]=top;
    }
    CDQ(1,top);
    for(int i=1;i<=top;++i)g[t[i].id]+=t[i].sz-1;
    for(int i=1;i<=N;++i)f[i]=g[bel[i]];
    for(int i=1;i<=N;++i)ans[f[i]]++;
    for(int i=0;i<N;++i)printf("%lld\n",ans[i]);
    return 0;
}

其实这里还有一个骚操作
一维相当于修改时间,二维就是修改和查询优先级
模板树状数组1

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
#define mid ((l+r)>>1)
const int _=500100;
inline int read()
{
    char ch='!';int z=1,num=0;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')z=-1,ch=getchar();
    while(ch<='9'&&ch>='0')num=(num<<3)+(num<<1)+ch-'0',ch=getchar();
    return z*num;
}
int N,M,top,cnt;
ll ans[_];
struct hand
{
    int p,v,op;
    bool operator <(const hand &A)const
        {
            if(p!=A.p)return p<A.p;
            return op<A.op;
        }
}t[_<<2],tmp[_<<2];
void CDQ(int l,int r)
{
    if(l==r)return;
    CDQ(l,mid);CDQ(mid+1,r);
    int p=l,q=mid+1,o=l-1;
    ll sum=0;
    while(p<=mid&&q<=r)
    {
        if(t[p]<t[q])
        {
            if(t[p].op==1)sum+=t[p].v;
            tmp[++o]=t[p++];
        }
        else
        {
            if(t[q].op==2)ans[t[q].v]-=sum;
            else if(t[q].op==3)ans[t[q].v]+=sum;
            tmp[++o]=t[q++];
        }
    }
    while(p<=mid)tmp[++o]=t[p++];
    while(q<=r)
    {
        if(t[q].op==2)ans[t[q].v]-=sum;
        else if(t[q].op==3)ans[t[q].v]+=sum;
        tmp[++o]=t[q++];
    }
    for(int i=l;i<=r;++i)
        t[i]=tmp[i];
}
int main()
{
    N=read();M=read();
    for(int i=1;i<=N;++i)
        t[++top]=(hand){i,read(),1};
    for(int i=1;i<=M;++i)
    {
        int op=read(),x=read(),y=read();
        if(op&1)
            t[++top]=(hand){x,y,1};
        else
        {
            cnt++;
            t[++top]=(hand){x-1,cnt,2};
            t[++top]=(hand){y,cnt,3};
        }
    }
    CDQ(1,top);
    for(int i=1;i<=cnt;++i)
        printf("%lld\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值