1. 程式人生 > >陌上花開(三維偏序)

陌上花開(三維偏序)

printf 分享 res lose using 輸入輸出格式 names std 樹狀數組

其實還是比較好理解的qaq。。。

先來看題:

題目描述

n 個元素,第 i 個元素有 ai?bi?ci? 三個屬性,設 f(i) 表示滿足 aj?ai?bj?bi?cj?ci?j 的數量。

對於d[0,n),求 f(i)=d 的數量

輸入輸出格式

輸入格式:

第一行兩個整數 n、k,分別表示元素數量和最大屬性值。

之後 n 行,每行三個整數 ai?bi?ci?,分別表示三個屬性值。

輸出格式:

輸出 n 行,第 d+1 行表示 f(i) = di 的數量。

輸入輸出樣例

輸入樣例#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

說明

1 \leq n \leq 100000, 1 \leq k \leq 2000001n100000,1k200000

作為CDQ的一道板子題,我們先來明確一下到底什麽是CDQ?

CDQ通俗一點說就是三句話:

1.遞歸前一半技術分享圖片

2.判斷前一半對於後一半的影響

3.遞歸後一半

那麽我們來看這道經典的三維偏序題,說一下大體的思路:

首先我們可以以x作為第一關鍵字進行排序,(y和z作為次要關鍵字),這樣我們就可以保證從左到右是以x單調遞增的。

然後我們將這個區間一分為二,分別對於每一個序列以y作為第一關鍵字排序,這樣一來我們得到的兩個序列擁有以下的性質:

1.左序列的任意x值都小於右序列的任意x值

2.每一個序列裏的y值都是單調遞增的

最後我們分別在序列頭和序列中點設定兩個指針j和i,對於每一個i,j都從頭開始枚舉,將j對應的y小於i對應的y的j加入樹狀數組中,最後只要判斷z值是否滿足條件就可以了。

當然,這道題還有一些小細節:

1.由於原序列可能會有重復的數,我們要先進行去重,並儲存每一個數都代表了幾個相同的數。

2.每一次i的增加都要清空樹狀數組,只要加上之前加的相反數就可以了。

好像也就這些了。。。

最後,附上本題代碼:

  1 #include<cstdio>
  2 #include<algorithm>
  3
#define maxn 100010 4 #define maxk 200010 5 using namespace std; 6 struct node 7 { 8 int x,y,z,ans,w; 9 }; 10 node a[maxn],b[maxn]; 11 int n,cnt[maxk]; 12 int k,z; 13 bool cmp1(node u,node v) 14 { 15 if(u.x==v.x) 16 { 17 if(u.y==v.y) 18 return u.z<v.z; 19 return u.y<v.y; 20 } 21 return u.x<v.x; 22 } 23 bool cmp2(node u,node v) 24 { 25 if(u.y==v.y) 26 return u.z<v.z; 27 return u.y<v.y; 28 } 29 struct TREE 30 { 31 int tre[maxk],kk; 32 int lowbit(int x) 33 { 34 return x&(-x); 35 } 36 int ask(int i) 37 { 38 int ans=0; 39 for(; i; i-=lowbit(i)) 40 { 41 ans+=tre[i]; 42 } 43 return ans; 44 } 45 void add(int i,int k) 46 { 47 for(; i<=kk; i+=lowbit(i)) 48 { 49 tre[i]+=k; 50 } 51 } 52 } t; 53 void cdq(int l,int r) 54 { 55 if(l==r) 56 { 57 return; 58 } 59 int mid=(l+r)>>1; 60 cdq(l,mid); 61 cdq(mid+1,r); 62 sort(a+l,a+mid+1,cmp2); 63 sort(a+mid+1,a+r+1,cmp2); 64 int i=mid+1,j=l; 65 while(i<=r) 66 { 67 while(a[j].y<=a[i].y && j<=mid) 68 { 69 t.add(a[j].z,a[j].w); 70 j++; 71 } 72 a[i].ans+=t.ask(a[i].z); 73 i++; 74 } 75 for(i=l; i<j; i++) 76 { 77 t.add(a[i].z,-a[i].w); 78 } 79 } 80 int main() 81 { 82 scanf("%d%d",&z,&k);//z是數量,k是最大屬性值 83 t.kk=k;//設定上限,t是維護的樹狀數組 84 for(int i=1; i<=z; i++) 85 { 86 scanf("%d%d%d",&b[i].x,&b[i].y,&b[i].z);// 87 } 88 sort(b+1,b+z+1,cmp1);//排序 89 int c=0; 90 for(int i=1; i<=z; i++) 91 { 92 c++; 93 if(b[i].x!=b[i+1].x || b[i].y!=b[i+1].y || b[i].z!=b[i+1].z ) 94 a[++n]=b[i],a[n].w=c,c=0; 95 }//去重 96 cdq(1,n);//cdqaq 97 for(int i=1; i<=n; i++) 98 { 99 cnt[a[i].ans+a[i].w-1]+=a[i].w;//這個地方不太好理解:cnt【x】就是儲存f【i】= x的個數,x就等於i的答案加上它重復的個數(可以取等)減去本身
100 } 101 for(int i=0; i<z; i++) 102 { 103 printf("%d\n",cnt[i]); 104 } 105 return 0; 106 }

陌上花開(三維偏序)