bzoj 3262 陌上花开 - CDQ分治 - 树状数组
Description
有n朵花,每朵花有三个属性:花形(s)、颜色(c)、气味(m),又三个整数表示。现要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量。定义一朵花A比另一朵花B要美丽,当且仅当Sa>=Sb,Ca>=Cb,Ma>=Mb。显然,两朵花可能有同样的属性。需要统计出评出每个等级的花的数量。
Input
第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,000 ), 分别表示花的数量和最大属性值。
以下N行,每行三个整数si, ci, mi (1 <= si, ci, mi <= K),表示第i朵花的属性
Output
包含N行,分别表示评级为0...N-1的每级花的数量。
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
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
1
3
0
1
0
1
0
0
1
HINT
1 <= N <= 100,000, 1 <= K <= 200,000
Source
题目大意 三维偏序计数。
首先偏序问题对于5维以下,通常有两种做法
1)k-1层套树(我表示拒绝写这类程序,宁可写O(n2k)大暴力骗分,都不会去写这种"正解")
2)CDQ分治 + (k - 2)层套树(比上面那种方法好多了)
对于3维偏序,显然法2更优秀。可以参考上一道题(bzoj 1176)的做法。
按c维第1关键字,m为第二关键字,s为第三关键字进行排序。
对s进行CDQ分治。统计s在[l, mid]中的元素对[mid + 1, r]中的元素的贡献。用和上一道题同样的做法,将(c, m)看成一个点,左区间中的点看成修改操作,将点(c, m)的权值 + 1,右区间中的点看成查询一个子矩阵的点权和。然后做法一模一样。
当l == r的时候就当成2维偏序问题,用树状数组水过。此时注意对于重复的判断。
Code
1 /** 2 * bzoj 3 * Problem#3262 4 * Accepted 5 * Time:1628ms 6 * Memory:9012k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 typedef class Data { 13 public: 14 int val[3]; 15 int id; 16 17 Data():val({0, 0, 0}), id(0) { } 18 19 friend boolean operator < (const Data& a, const Data &b) { 20 for(int i = 1; i < 3; i++) 21 if(a.val[i] != b.val[i]) 22 return a.val[i] < b.val[i]; 23 return a.val[0] < b.val[0]; 24 } 25 26 boolean operator == (Data b) { 27 return val[0] == b.val[0] && val[1] == b.val[1] && val[2] == b.val[2]; 28 } 29 }Data; 30 31 #define lowbit(x) ((x) & (-x)) 32 33 typedef class IndexedTree { 34 public: 35 int s; 36 int *lis; 37 38 IndexedTree():lis(NULL), s(0) { } 39 IndexedTree(int s):s(s) { 40 lis = new int[(s + 1)]; 41 memset(lis, 0, sizeof(int) * (s + 1)); 42 } 43 44 inline void add(int idx, int x) { 45 for(; idx <= s; idx += lowbit(idx)) 46 lis[idx] += x; 47 } 48 49 inline int getSum(int idx) { 50 int rt = 0; 51 for(; idx; idx -= lowbit(idx)) 52 rt += lis[idx]; 53 return rt; 54 } 55 }IndexedTree; 56 57 int n, K; 58 vector<Data> ds; 59 IndexedTree it; 60 int *scores; 61 int *cnts; 62 63 inline void init() { 64 scanf("%d%d", &n, &K); 65 scores = new int[(n + 1)]; 66 cnts = new int[(n + 1)]; 67 memset(scores, 0, sizeof(int) * (n + 1)); 68 memset(cnts, 0, sizeof(int) * (n + 1)); 69 Data d; 70 for(int i = 0; i < n; i++) { 71 for(int j = 0; j < 3; j++) 72 scanf("%d", &d.val[j]); 73 d.id = i; 74 ds.push_back(d); 75 } 76 } 77 78 void CDQDividing(int l, int r, vector<Data> &q) { 79 if(q.empty()) return; 80 if(l == r) { 81 for(int i = 0, j; i < (signed)q.size(); i = j) { 82 it.add(q[i].val[2], 1); 83 for(j = i + 1; j < (signed)q.size() && q[j] == q[j - 1]; j++) 84 it.add(q[j].val[2], 1); 85 int c = it.getSum(q[i].val[2]); 86 for(int k = i; k < j; k++) 87 scores[q[k].id] += c - 1; 88 } 89 for(int i = 0; i < (signed)q.size(); i++) 90 it.add(q[i].val[2], -1); 91 return; 92 } 93 94 int mid = (l + r) >> 1; 95 vector<Data> ql, qr; 96 for(int i = 0; i < (signed)q.size(); i++) { 97 if(q[i].val[0] <= mid) 98 it.add(q[i].val[2], 1), ql.push_back(q[i]); 99 else 100 scores[q[i].id] += it.getSum(q[i].val[2]), qr.push_back(q[i]); 101 } 102 103 for(int i = 0; i < (signed)ql.size(); i++) 104 it.add(ql[i].val[2], -1); 105 106 q.clear(); 107 CDQDividing(l, mid, ql); 108 CDQDividing(mid + 1, r, qr); 109 } 110 111 inline void solve() { 112 sort(ds.begin(), ds.end()); 113 it = IndexedTree(K); 114 // for(int i = 0; i < n; i++) 115 // printf("%d %d %d\n", ds[i].val[0], ds[i].val[1], ds[i].val[2]); 116 CDQDividing(1, K, ds); 117 for(int i = 0; i < n; i++) 118 cnts[scores[i]]++; 119 for(int i = 0; i < n; i++) 120 printf("%d\n", cnts[i]); 121 } 122 123 int main() { 124 init(); 125 solve(); 126 return 0; 127 }