洛谷 3810: 三維偏序


——三維偏序
原題傳送門

Description

因爲洛谷題目…好像沒法複製,所以簡單闡述題目大意.

nn個元素,第ii個元素xix_i33個屬性ai,bi,cia_i,b_i,c_i,(a,b,c[1,k]a,b,c \in [1,k])
f(i)f(i) 表示滿足 ajai,bjbi,cjcia_j \leqslant a_i,b_j \leqslant b_i, c_j \leqslant c_ijj 的數量.
求對於d[1,k]d \in[1,k],滿足 f(i)=df(i)=dii 有多少個.

Data

Input
第一行兩個整數 nn , kk,分別表示元素數量和最大屬性值。
之後 nn 行,每行三個整數 ai,bi,cia_i,b_i,c_i ,分別表示三個屬性值。
Output
輸出 nn 行,第 d+1d+1 行表示 f(i)=df(i)=dii 的數量。

	Sample Input
	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
	Sample Output
	3
	1
	3
	0
	1
	0
	1
	0
	0
	1

1n100000,1k2000001 \leqslant n \leqslant 100000,1 \leqslant k \leqslant 200000.

思路

三維偏序模板題.

n維偏序

三維偏序,屬於一種名爲"nn 維偏序"的題目.
mm 維偏序形式化地描述如下:

nn個元素{x1,x2,x3,xn}.\{x_1,x_2,x_3\dots,x_n\}.
其中xix_imm個分量{a1,a2am}.\{a_1,a_2\dots a_m\}.
f(i)={cnt(j)k,xjakxiak}f(i)=\{cnt(j)|\forall k, x_{j_{a_k}}\leqslant x_{i_{a_k}}\}.

不那麼形式化地:
可以參考Description.
就是有一堆元素,每個元素有mm個屬性.
f(i)f(i)表示每一個元素都小於等於xix_i的屬性的個數.

下方例子僅供參考.
比如有幾個人:(33維偏序)
XQQ先生: 算法,編碼都很優秀.(不愧是XQQ),作死能力…(他一向就是那麼優秀)
ZACK先生: 算法優秀,編碼一般,作死能力一般
Mr.F: 算法優秀,編碼…,作死能力一般
本人: 算法一般,編碼一般.,作死能力極強~~(每次都被抓)~~
(作死能力倒序).
那麼可以說:
f(XQQ)=3f(ZACK)=2,f(F)=f(Me)=0f(XQQ)=3,f(ZACK)=2,f(F)=f(Me)=0

n維偏序的時間複雜度.

這裏列舉一下各維的時間複雜度
n=1n=1,emm好像似乎大概排序就可以O(nlogn)O(nlogn)
n=2n=2,就是求逆序對.O(nlogn)O(nlogn)
n=3n=3,這就是我們的重點.事實證明可以是O(nlog2n)O(nlog^2n)

三維偏序的暴力做法.

快往這裏看這裏是正解.
暴力做法是十分trival 的.
就像當年做逆序對一樣O(n2)O(n^2)的暴力枚舉ai,aja_i,a_j.
但是這個數據肯定會卡你啊…
所以我們就要優秀優化一波.
這裏有很多種優化方法:

bitsetbitset:我不會
KDTreeK-DTree:我不會
CDQCDQ分治.

由於我很菜各種原因,這裏直接介紹CDQCDQ分治.

CDQ分治

CDQ分治,就是用前面的操作更新後面的操作.
並且更好的是它可以不停地套用.
和二分類似,它也將操作區間不斷二分,更新
所以我們可以這樣一波操作.
不斷將詢問二分.
然後按照"左更右"的原則:

  1. 先將所有元素按axa_x 排序.
  2. 分治後對區間排序,這樣就會保證aya_y 有序.
  3. 如果原來是axmida_x\leqslant mid,那麼就會對後面的產生貢獻.
  4. 然後還需要判斷aza_z.
  5. 這時候參考逆序對的"二維偏序"就可以了.

所以我們震驚的發現,"三維偏序"可以依靠"二維偏序"實現?
這個規則是否適用於更高維度?
答案是肯定的!
在這裏插入圖片描述所以我們可以用CDQ套CDQ(套CDQ套CDQ套///).
但是每套一個CDQ就會有一個loglog,所以四維偏序的複雜度(使用CDQ)應該是O(nlog3n)O(nlog^3n).
雖然這個還是很優秀,但是好像在n104n \leqslant 10^4之內好像並沒有什麼用.
但是對於三維偏序,這種算法還是綽綽有餘.

Code

#include<bits/stdc++.h>
#define lowbit(x) x&(-x)
using namespace std;
const int N=1e5;
struct node
{
	int x,y,z,w,ans;
}a[N],tmp[N];
int c[2*N];
int n,k,cnt;
int ans[N]; 
void add(int x,int val)
{
	for (;x<=k;x+=lowbit(x)) c[x]+=val;
}
int sum(int x)
{
	int ans=0;
	for (;x>0;x-=lowbit(x)) ans+=c[x];
	return ans;
}
bool same(const node a,const node b)
{
	return a.x==b.x&&a.y==b.y&&a.z==b.z;
}
bool cmp1(const node &a,const node &b)
{
	if (a.x!=b.x) return a.x<b.x;
	if (a.y!=b.y) return a.y<b.y;
	return a.z<b.z;
}
bool cmp2(const node &a,const node &b)
{
	if (a.y!=b.y) return a.y<b.y;
	if (a.z!=b.z) return a.z<b.z;
	return a.x<b.x;
}
void CDQ(int L,int R)
{
	if (L==R) return;
	int mid=(L+R)>>1;
	CDQ(L,mid),CDQ(mid+1,R);
	sort(a+L,a+mid+1,cmp2),sort(a+mid+1,a+R+1,cmp2);
	int i=mid+1,j=L;
	for (;i<=R;i++)
	{
		while (a[j].y<=a[i].y&&j<=mid) add(a[j].z,a[j].w),j++;
		a[i].ans+=sum(a[i].z);
	}
	for (i=L;i<j;i++) add(a[i].z,-a[i].w);
}
int main()
{
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;i++) scanf("%d%d%d",&tmp[i].x,&tmp[i].y,&tmp[i].z);
	sort(tmp+1,tmp+n+1,cmp1);
	int p=0,c=0;
	for (int i=1;i<=n;i++)
	{
		c++;
		if (!same(tmp[i],tmp[i+1])) a[++p]=tmp[i],a[p].w=c,c=0;
	}
	int rn=n;
	n=p;
	CDQ(1,n);
	for (int i=1;i<=n;i++)
		ans[a[i].ans+a[i].w-1]+=a[i].w;
	for (int i=0;i<rn;i++) printf("%d\n",ans[i]);
	return 0;
}

感謝奆老關注 qwq ?
順便悼念一下我那逝去的博文…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章