万字长文·三维偏序/cdq分治
万字长文·三维偏序/cdq分治
cdq分治
静态区间问题
cdq分治是一种解决区间统计问题的常用方法
点分治相当于吧cdq分治挪到树上
cdq分治即把一个序列分为两部分 区间统计的量分为跨中点的 左边的 右边的
(一下所说统计均指在区间中点)
先递归处理左边的和右边的
然后再寻找某种\(O(n)\)或较优方式统计跨中点的
熟知归并排序求逆序对
这就是使用cdq分治解决二维偏序的例子
离线的带修数据结构问题
cdq 分治被称为 动态问题转化为静态问题的工具
发现离线的数据结构离线下来的操作是一个序列
每个修改操作会对它后面的询问操作产生影响
使用cdq分治来完成这个问题
先递归处理[l,mid],[mid+1,r]
然后加入[l,mid]中操作对[mid+1,r]中询问的影响 并得出答案
正确性:显然所有修改按时间顺序排序
一个例子
矩形加矩形求和¶
这里的矩形加矩形求和就是字面意思上的矩形加矩形求和,
让你维护一个二维平面,然后支持在一个矩形区域内加一个数字,
每次询问一个矩形区域的和
这个问题的不带修就是经典的扫描线问题
加入修改后只需按照cdq套路完成即可qwq
三维偏序
显然三维偏序问题是二维偏序的扩展
二维偏序有两种写法:归并排序(cdq分治)和树状数组
考虑结合二者
先对第一维排序
然后显然[l,mid]和[mid+1,r]只会在两者之间产生偏序
我们可以使用双指针扫左区间和右区间 同二维偏序一样
将左区间第二维小于右区间的第三维插入树状数组 将对应于左区间的右区间首个b<左区间点的点的c值在树状数组中查询前缀和
正确性与归并排序同理
细节:对于相同点 显然二者可以互相贡献答案 而该算法显然只能计算一次 所以要去重再把重值算回
代码
#include<bits/stdc++.h>
using namespace std;
#define N 200005
struct node
{
long long a,b,c,cnt,ans=0;
}q1[N],q2[N];
long long n,kk,asn[N];
bool cmp(node x,node y)
{
if(x.a==y.a)
{
if(x.b==y.b)
return x.c<y.c;
else
return x.b<y.b;
}
else return x.a<y.a;
}
bool cmp2(node x,node y)
{
if(x.b==y.b)
return x.c<y.c;
else
return x.b<y.b;
}
struct tyz
{
long long tr[N];
void change(long long x,long long k)
{
for(;x<=kk;x+=(x&(-x)))
tr[x]+=k;
}
long long ask(long long x)
{
long long ret=0;
for(;x>0;x-=(x&(-x)))
ret+=tr[x];
return ret;
}
void clear()
{
for(long long i=1;i<=kk;i++) tr[i]=0;
}
}qt;
void cdq(long long l,long long r)
{
if(l==r) return;
long long mid=(l+r)>>1;
cdq(l,mid),cdq(mid+1,r);
long long ls=l,rs=mid+1;long long i=l;
for(;i<=r&&ls<=mid&&rs<=r;)
{
if(q2[ls].b<=q2[rs].b)
qt.change(q2[ls].c,q2[ls].cnt),
q1[i].a=q2[ls].a,
q1[i].b=q2[ls].b,
q1[i].c=q2[ls].c,
q1[i].ans=q2[ls].ans,
q1[i].cnt=q2[ls].cnt,
i++,
ls++;
else
q2[rs].ans+=qt.ask(q2[rs].c),
q1[i].a=q2[rs].a,
q1[i].b=q2[rs].b,
q1[i].c=q2[rs].c,
q1[i].ans=q2[rs].ans,
q1[i].cnt=q2[rs].cnt,
i++,
rs++;
}
while(rs<=r)
{
q2[i].ans+=qt.ask(q2[rs].c),
q1[i].a=q2[rs].a,
q1[i].b=q2[rs].b,
q1[i].c=q2[rs].c,
q1[i].ans=q2[rs].ans,
q1[i].cnt=q2[rs].cnt,
i++;
rs++;
}
for(int i=l;i<ls;i++)
qt.change(q2[i].c,-q2[i].cnt);
while(ls<=mid)
{
q1[i].a=q2[ls].a,
q1[i].b=q2[ls].b,
q1[i].c=q2[ls].c,
q1[i].ans=q2[ls].ans,
q1[i].cnt=q2[ls].cnt,
i++;
ls++;
}
for(long long kkk=l;kkk<=r;kkk++)
{
q2[kkk].a=q1[kkk].a,
q2[kkk].b=q1[kkk].b,
q2[kkk].c=q1[kkk].c,
q2[kkk].ans=q1[kkk].ans,
q2[kkk].cnt=q1[kkk].cnt;
}
}
int main()
{
scanf("%d%d",&n,&kk);
for(long long i=1;i<=n;i++)
scanf("%d%d%d",&q1[i].a,&q1[i].b,&q1[i].c);
sort(q1+1,q1+1+n,cmp);
long long m=0,top=0;
for(long long i=1;i<=n;i++)
{
top++;
if(q1[i].a!=q1[i+1].a||q1[i].b!=q1[i+1].b||q1[i].c!=q1[i+1].c)
{
m++;
q2[m].a=q1[i].a;
q2[m].b=q1[i].b;
q2[m].c=q1[i].c;
q2[m].cnt=top;
top=0;
}
}
cdq(1,m);
for(long long i=1;i<=m;i++)
asn[q2[i].ans+q2[i].cnt-1]+=q2[i].cnt;
for(long long i=0;i<n;i++)
cout<<asn[i]<<endl;
return 0;
}