洛谷P3810【模板】三维偏序(陌上花开)

洛谷P3810【模板】三维偏序(陌上花开)

题目背景

这是一道模板题

可以使用bitset,CDQ分治,K-DTree等方式解决。

题目描述

有 n 个元素,第 i 个元素有 ai 、 bi 、 ci 三个属性,设 f(i) 表示满足 ajai 且 bjbi 且 cjci 的 j 的数量。

对于 d[0,n) ,求 f(i)=d 的数量

 

输入格式:

 

第一行两个整数 n 、 k ,分别表示元素数量和最大属性值。

之后 n 行,每行三个整数 ai 、 bi 、 ci ,分别表示三个属性值。

 

输出格式:

 

输出 n 行,第 d+1 行表示 f(i)=d 的 i 的数量。

 

输入输出样例

输入样例#1: 
10 3
3 3 3
2 3 3
2 3 1
3 1 1
3 1 2
1 3 1
1 1 2
1 2 2
1 3 2
1 2 1
输出样例#1: 
3
1
3
0
1
0
1
0
0
1

说明

1n100000,1k200000

题解:

今儿个新学了算法:cdq分治

此题为cdq分治三维偏序的模板题。

我们先按照第一维从小到大排序,相等的话比下一维,以此类推。

排完序后有个注意点,序列中的元素可能存在重复,于是要去重,代码见下:

1 for (int i=1; i<=n; i++)
2       if (a[i].x==a[i-1].x &&
3           a[i].y==a[i-1].y &&
4           a[i].z==a[i-1].z) a[m].t++;
5       else a[++m]=a[i];    

其中 a[i].t 存的是与之相等的元素的个数(包括自己)。

接下来就开始cdq了:

消除了第一维的影响,我们只需按第二维从小到大归并,第三维用树状数组维护就行了。

还有一个要点(千万别忘了): 树状数组的数组记得清空,也不需要全部memset,只需把修改过的节点清空就可以了,效率大大提高,如下:

for (int i=l; i<=mid; i++) change(a[i].z,-a[i].t);

就讲到这里吧,代码如下:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=100005,K=200005,inf=0x3f3f3f3f;
 4 struct node{
 5     int x,y,z,t,s;
 6     bool operator < (const node &aa) const {
 7         return x==aa.x?(y==aa.y?z<aa.z:y<aa.y):x<aa.x;
 8     }
 9 }a[N];
10 int n,m,k,ans[N],cnt[K];
11 inline int read()
12 {
13     int x=0,f=1;
14     char ch=getchar();
15     while (!isdigit(ch))
16       f=(ch=='-1')?-1:1,ch=getchar();
17     while (isdigit(ch))
18       x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
19     return x*f;
20 }
21 node tmp[N];
22 inline int lowbit(int x)
23 {
24     return x&(-x);
25 }
26 void change(int x,int w)
27 {
28     for (int i=x; i<=k; i+=lowbit(i))
29       cnt[i]+=w;
30 }
31 inline int get(int x)
32 {
33     int sum=0;
34     for (int i=x; i; i-=lowbit(i))
35       sum+=cnt[i];
36     return sum;
37 }
38 void cdq(int l,int r)
39 {
40     if (l==r) return;
41     int mid=(l+r)>>1;
42     cdq(l,mid),cdq(mid+1,r);
43     int u=l,v=mid+1;
44     for (int i=l; i<=r; i++)
45       if (v>r || (a[u].y<=a[v].y && u<=mid))
46         change(a[u].z,a[u].t),tmp[i]=a[u++];
47       else a[v].s+=get(a[v].z),tmp[i]=a[v++];
48     for (int i=l; i<=mid; i++) change(a[i].z,-a[i].t);
49     for (int i=l; i<=r; i++) a[i]=tmp[i];
50 }
51 int main()
52 {
53     n=read(),k=read(),m=0,a[0].x=a[0].y=a[0].z=-inf;
54     for (int i=1; i<=n; i++)
55       a[i].x=read(),a[i].y=read(),a[i].z=read(),a[i].t=1;
56     sort(a+1,a+1+n);
57     for (int i=1; i<=n; i++)
58       if (a[i].x==a[i-1].x &&
59           a[i].y==a[i-1].y &&
60           a[i].z==a[i-1].z) a[m].t++;
61       else a[++m]=a[i];    
62     cdq(1,m);
63     for (int i=1; i<=m; i++) ans[a[i].t+a[i].s-1]+=a[i].t;
64     for (int i=0; i<n; i++) printf("%d
",ans[i]);
65     return 0;
66 }
View Code

 这里再补充点洛谷P3157动态逆序对的解法:

首先动态操作比较麻烦,想法办转成静态的:

我们让被删除的数排在未被删除的数的后面,删除早的数排在删除晚的数的后面,这样一来左边的右边的就不会对左边的产生影响,而左边的仍旧可以对右边产生影响,于是就OK了。

还有一个要注意的是:按照这样排序后,以下两种情况都是符合逆序对的:

xi<xj && yi>yj

xi>xj && yi<yj

要用两个树状数组分别维护。

代码就不贴了,可参见其他dalao。

加油加油加油!!! fighting fighting fighting !!!

原文地址:https://www.cnblogs.com/Frank-King/p/9159561.html