bzoj3262 & 洛谷P3810 陌上花开 CDQ分治+树状数组解决三维偏序

题目链接:
洛谷3810
bzoj3262

题目要求我们计算这样一个东西:
一个序列有 n n n个元素,每个元素有三个权值 a i , b i , c i . a_i,b_i,c_i. ai,bi,ci. 对于每个权值,需要求出有多少个 j j j,满足 a j ≤ a i , b j ≤ b i , c j ≤ c i . a_j\leq a_i,b_j\leq b_i,c_j\leq c_i. ajai,bjbi,cjci. ——三维偏序
数据范围: n &lt; = 1 0 5 , a i , b i , c i &lt; = 2 ∗ 1 0 5 n&lt;=10^5, a_i,b_i,c_i&lt;=2*10^5 n<=105,ai,bi,ci<=2105

首先来看一维偏序:
逆序对?记录下原来的位置 p o s pos pos,然后直接 s o r t sort sort一遍……

二维偏序:
发现直接 s o r t sort sort一遍不行……
先以 a a a为第一关键字,以 b b b为第二关键字,从小到大 s o r t sort sort一遍。
然后从左到右扫过去,对于第 i i i个元素,统计 [ 1 , i − 1 ] [1,i-1] [1,i1]中有多少个元素的 b b b比它的 b b b小,用树状数组维护即可。

三维偏序:
发现好像没法用 s o r t sort sort+树状数组大力求了……

用树状数组套动态开点线段树或者树状数组套平衡树做。 ——巨神mhy

给巨神 m h y mhy mhy的博客打个广告qwq:前往膜拜julao

然而我并不想写树套树……然后就学了一下 C D Q CDQ CDQ分治……发现常数蜃小qwq
C D Q CDQ CDQ分治思路:
s o r t sort sort一遍,然后问题转化成一个 n n n个元素的序列,对于每个元素,有多少个 j j j,满足 j ≤ i , b j ≤ b i , c j ≤ c i . j\leq i , b_j\leq b_i , c_j\leq c_i. ji,bjbi,cjci.
因为是cdq分治,所以要分治qwq
把假设当前序列为 [ l , r ] [l,r] [l,r] ( l &lt; r ) (l&lt;r) (l<r),那么序列分为 [ l , m i d ] [l,mid] [l,mid] [ m i d + 1 , r ] [mid+1,r] [mid+1,r]
首先递归求解两个子序列,然后考虑合并。
把两个子序列分别以 b b b为第一关键字,以 c c c为第二关键字从小到大排序。
以下面这个序列为例:
在这里插入图片描述
枚举右边的子序列的每一个元素,假设当前枚举到的元素编号为 i i i
因为右边子序列内部对 i i i的影响已经递归求解好,所以只用考虑 [ l , m i d ] [l,mid] [l,mid]中的元素对它的贡献。
假设当编号为 i i i时左边子序列的元素 b b b值小于它的 b b b值的的区间是 [ l , p r e p t r ] . [l,preptr]. [l,preptr].
当编号枚举到 i + 1 i+1 i+1时左边子序列元素 b b b值小于它的区间是 [ l , n o w p t r ] [l,nowptr] [l,nowptr].
因为排序后, b b b值单调不减,所以 b [ i + 1 ] &gt; = b [ i ] . b[i+1]&gt;=b[i]. b[i+1]>=b[i]. b [ n o w p t r ] &gt; = b [ p r e p t r ] , b[nowptr]&gt;=b[preptr], b[nowptr]>=b[preptr], n o w p t r &gt; = p r e p t r . nowptr&gt;=preptr. nowptr>=preptr.
所以 p t r ptr ptr指针是不减的qwq。

所以大力扫过去,左边维护一个不减的指针 p t r ptr ptr,在 [ l , p t r ] [l,ptr] [l,ptr]中用树状数组维护有多少个元素 c c c值比当前元素 c c c值小即可。

模拟一下上面那张图:
i = 4 i=4 i=4 ,此时 b i = 3 , c i = 2 b_i=3,c_i=2 bi=3,ci=2
此时 p t r = 1 ptr=1 ptr=1,把 c 1 = 4 c_1=4 c1=4加入树状数组。
然后查询树状数组中有多少个 &lt; = 2 &lt;=2 <=2,显然是 0. 0. 0. 0 0 0加到 a n s [ 4 ] ans[4] ans[4]中。

i = 5 i=5 i=5 ,此时 b i = 5 , c i = 5 b_i=5,c_i=5 bi=5,ci=5
此时 p t r = 2 ptr=2 ptr=2,把 c 2 = 3 c_2=3 c2=3加入树状数组。
然后查询树状数组中有多少个 &lt; = 5 &lt;=5 <=5,显然是 2. 2. 2. 2 2 2加到 a n s [ 5 ] ans[5] ans[5]中。

i = 6 i=6 i=6 ,此时 b i = 6 , c i = 3. b_i=6,c_i=3. bi=6,ci=3.
此时 p t r = 2 ptr=2 ptr=2,不把任何数加入树状数组。
然后查询有多少个 &lt; = 3 &lt;=3 <=3,显然是 1. 1. 1. 1 1 1加到 a n s [ 6 ] ans[6] ans[6]中。

时间复杂度:
注:我也不确定对不对qwq
T ( n ) = 2 T ( n 2 ) + O ( n l o g k ) \large T(n)=2T(\frac{n}{2})+O(nlogk) T(n)=2T(2n)+O(nlogk)(好像是这样?)
T ( n ) = O ( n l o g n l o g k ) T(n)=O(nlognlogk) T(n)=O(nlognlogk) (大概是这样了qwq)

就没了吗? n a i v e ! naive! naive!
要注意两点:

1. 1. 1.发现当 a i = a j , b i = b j , c i = c j a_i=a_j,b_i=b_j,c_i=c_j ai=aj,bi=bj,ci=cj的时候,会出一些奇奇怪怪的问题。
所以要再记录一个 v i s i vis_i visi,表示三元组 ( a i , b i , c i ) (a_i,b_i,c_i) (ai,bi,ci)出现过多少次。
然后最后 ( a i , b i , c i ) (a_i,b_i,c_i) (ai,bi,ci)的答案还要加上 v i s i − 1 vis_i-1 visi1

2. 2. 2.每次分治完,要把树状数组清空

毒瘤代码

#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#define re register int
#define rl register ll
using namespace std;
typedef long long ll;
int read() {
	re x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9') {
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=10*x+ch-'0';
		ch=getchar();
	}
	return x*f;
}
namespace I_Love {

const int Size=200005;
const int LOG=20;
int n,k,ans[Size],out[Size];
struct Flower {
	int id,a,b,c,vis,ans;
} tmp[Size],w[Size];
//int vis[Size];
inline bool compa(Flower x,Flower y) {
	if(x.a!=y.a)	return x.a<y.a;
	if(x.b!=y.b)	return x.b<y.b;
	return x.c<y.c;
}
int tree[Size];
inline int lowbit(int x) {
	return x&(-x);
}
inline void update(int x,int val) {
	for(re i=x; i<=k; i+=lowbit(i)) {
		tree[i]+=val;
	}
}
inline int query(int x) {
	int tot=0;
	for(re i=x; i; i-=lowbit(i)) {
		tot+=tree[i];
	}
	return tot;
}
inline bool compb(Flower x,Flower y) {
	if(x.b!=y.b)	return x.b<y.b;
	return x.c<y.c;
}
void CDQ_Divide(int l,int r) {
	if(l==r)	return;
	int mid=(l+r)>>1;
	CDQ_Divide(l,mid);
	CDQ_Divide(mid+1,r);
	sort(w+l,w+1+mid,compb);
	sort(w+1+mid,w+1+r,compb);
	int ptr=l-1;
	for(re i=mid+1; i<=r; i++) {
		while(ptr<mid && w[ptr+1].b<=w[i].b) {
			ptr++;
			update(w[ptr].c,w[ptr].vis);
		}
		w[i].ans+=query(w[i].c);
	}
	for(re i=l; i<=ptr; i++) {
		update(w[i].c,-w[i].vis);
	}
}
void Fujibayashi_Ryou() {
//	freopen("data.txt","r",stdin);
//	freopen("WA.txt","w",stdout);
	n=read();
	k=read();
	for(re i=1; i<=n; i++) {
		tmp[i].a=read();
		tmp[i].b=read();
		tmp[i].c=read();
		tmp[i].id=i;
	}
	sort(tmp+1,tmp+1+n,compa);
	int cnt=1;
	w[1]=tmp[1]; w[1].vis=1;
	//去重 
	for(re i=2; i<=n; i++) {
		if(tmp[i].a!=tmp[i-1].a || tmp[i].b!=tmp[i-1].b || tmp[i].c!=tmp[i-1].c) {
			w[++cnt]=tmp[i];
			w[cnt].vis=1;
		} else {
			w[cnt].vis++;
		}
	}
	CDQ_Divide(1,cnt);
	sort(w+1,w+1+cnt,compa);
	for(re i=1; i<=cnt; i++) {
//		printf("%d %d %d %d\n",w[i].a,w[i].b,w[i].c,w[i].ans+w[i].vis-1);
		out[w[i].ans+w[i].vis-1]+=w[i].vis;
	}
	for(re i=0; i<n; i++) {
		printf("%d\n",out[i]);
	}
}

}
int main() {
	I_Love::Fujibayashi_Ryou();
	return 0;
}
/*
10 3
1 2 2
2 2 1
2 2 3
2 2 3
2 3 3
3 2 1
3 2 1
3 2 1
3 2 2
3 3 3
*/
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值