万字长文·三维偏序/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;
}
posted @ 2021-06-16 20:24  禁止右转  阅读(99)  评论(0编辑  收藏  举报