Fork me on GitHub

【高级数据结构】cdq分治(陌上花开·三维偏序)

【高级数据结构】cdq分治(陌上花开·三维偏序)

一、陌上花开

题目链接:陌上花开

对于这道题我们需要求三维偏序中每个元素比其小的元素个数

  • 我们先对数组进行排序,$x$ 为第一关键字,$y$ 为第二关键字,$z$ 为第三关键字,从小到大排序
 1 struct Point{
 2     int x,y,z,ans,num;
 3     bool operator < (const Point &rhs) const
 4     {
 5         return x!=rhs.x?x<rhs.x:(y!=rhs.y?y<rhs.y:z<rhs.z);
 6     }
 7     bool operator != (const Point &rhs) const
 8     {
 9         return x!=rhs.x||y!=rhs.y||z!=rhs.z;
10     }
11 }A[MAXN],a[MAXN],b[MAXN];
  • 然后是去重,因为这道题是求带等号的偏序
1 for (int i=1;i<=n;i++)
2 {
3     if (A[i]!=A[i-1]) a[++tot]=A[i];
4     a[tot].num++;
5 }

 

 

  • 接着就是算法的主题cdq分治部分

分治的意义是分而治之,将数据规模大的问题分解为若干个规模较小的但解决方法一样的子问题,直到子问题能够在常数时间内解决,大问题的答案有子问题在线性时间内合并而成。

我们先考虑结束情况,当 $l = r$ 是不会产生贡献,即为结束状态。

我们在考虑如何合并两个子问题

由于第一次的排序,我们已经保证 $[l,mid]$ (左)区间的 $x$ 均小于等于 $[mid+1,r]$ (右)区间的 $x$

我们考虑一般的归并排序思路按 $y$ 合并数组

在合并的过程中,我们发现已经合并的数组中 $x$ $y$ 的值均小于 $[mid+1,r]$ 区间的 $x$ $y$

当一个右区间开头的数小于左区间开头的数,该左区间已经合并的数中 $x$ $y$ 都小于右区间的开头的 $x$ $y$,所以我们只要维护已合并的左区间中 $z$ 小于右区间开头的 $z$ 的个数即可

我们选择使用常数优越的树状数组维护。

时间复杂度 $O(N log^{2} N)$

 1 #include<bits/stdc++.h>
 2 #define MAXK 200010
 3 #define MAXN 100010
 4 using namespace std;
 5 inline int read ()
 6 {
 7     int s=0,w=1;
 8     char ch=getchar ();
 9     while (ch<'0'||ch>'9'){if (ch=='-') w=-1;ch=getchar ();}
10     while ('0'<=ch&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar ();
11     return s*w;
12 }
13 struct Point{
14     int x,y,z,ans,num;
15     bool operator < (const Point &rhs) const
16     {
17         return x!=rhs.x?x<rhs.x:(y!=rhs.y?y<rhs.y:z<rhs.z);
18     }
19     bool operator != (const Point &rhs) const
20     {
21         return x!=rhs.x||y!=rhs.y||z!=rhs.z;
22     }
23 }A[MAXN],a[MAXN],b[MAXN];
24 int n,k,tot;
25 int c[MAXK],f[MAXN],num[MAXN];
26 bool used[MAXN];
27 int lowbit (int x)
28 {
29     return x&-x;
30 }
31 void add (int x,int val)
32 {
33     for (int i=x;i<=k;i+=lowbit (i)) c[i]+=val;
34 }
35 int query (int x)
36 {
37     int sum=0;
38     for (int i=x;i>=1;i-=lowbit (i)) sum+=c[i];
39     return sum;
40 }
41 void cdq (int l,int r)
42 {
43     if (l==r) return;
44     int mid=(l+r)>>1;
45     cdq (l,mid),cdq (mid+1,r);
46     int i=l,j=mid+1;
47     for (int k=l;k<=r;k++)
48     {
49         if (a[i].y<=a[j].y&&i<=mid)
50         {
51             add (a[i].z,a[i].num);
52             b[k]=a[i++];
53             used[k]=1;
54         }
55         else if (a[i].y>a[j].y&&j<=r)
56         {
57             a[j].ans+=query (a[j].z);
58             b[k]=a[j++];
59         }
60         else if (i>mid)
61         {
62             a[j].ans+=query (a[j].z);
63             b[k]=a[j++];
64         }
65         else b[k]=a[i++];
66     }
67     for (int i=l;i<=r;i++) a[i]=b[i];
68     for (int i=l;i<=r;i++)
69         if (used[i]) add (a[i].z,-a[i].num),used[i]=0; 
70 }
71 int main()
72 {
73     n=read ();k=read ();
74     for (int i=1;i<=n;i++) A[i].x=read (),A[i].y=read (),A[i].z=read ();
75     sort (A+1,A+n+1);
76     for (int i=1;i<=n;i++)
77     {
78         if (A[i]!=A[i-1]) a[++tot]=A[i];
79         a[tot].num++;
80     }
81     cdq (1,tot);
82     for (int i=1;i<=n;i++) f[a[i].ans+a[i].num-1]+=a[i].num;
83     for (int i=0;i<n;i++) printf ("%d\n",f[i]);
84     return 0;
85 }

 

posted @ 2018-12-06 15:05  Paul·Shi  阅读(260)  评论(0编辑  收藏  举报