虽然说好像这题有其他做法,但是在问题转化之后,使用CDQ分治是显而易见的
并且如果CDQ打的熟练的话,码量也不算大,打的也很快,思维难度也很小
没学过CDQ分治的话,可以去看看我的另一篇博客,是CDQ分治的入门教程
下面是正文:
首先整理一下条件:
每个点有三个属性,x,r,f
统计有多少对点i,j满足 min(ri,rj) >= |xi-xj| 且 |fi-fj| <= k,这样的点对被称作是“坏的”
对r值取min是个烦人的条件,于是我们把点按照r值从大到小排序,按照r值从大到小的顺序依次考虑每个点
这样对于每个点,我们只考虑它之前出现的点,也就是r值比他大的点,和他能不能组成“坏的”点对
这样的话,因为一个点i之前所有的点j的r值都比他大,所以 min(ri,rj) = ri
然后我们重新看一下问题:
按照指定的顺序依次加入点,每次加入一个点i,考虑它之前加入的所有点j,有多少个点满足 |xi-xj| <= ri 且 |fi-fj| <= k
再转化一下:
对于每个点i,考虑有多少个它之前的点j满足 xi-ri <= xj <= xi+ri 且 fi-k <= fj <= fi+k
我们把x和f这两个属性看做二维平面中的横纵坐标,问题就变成了:
向一个平面中添加一个点,查询指定矩形内点的个数
这是一个经典的三维偏序问题,可以用 线段树套线段树 或者 CDQ分治 来做
代码如下:
1 #include <iostream> 2 #include <cstring> 3 #include <algorithm> 4 #include <cstdio> 5 #include <cstdlib> 6 #include <cassert> 7 #include <cctype> 8 #include <cmath> 9 #include <vector> 10 #include <queue> 11 #include <set> 12 #include <map> 13 14 using namespace std; 15 typedef long long ll; 16 const int MAXN = 100010; 17 const int MAXF = 10020; 18 19 int n, k; 20 21 struct Item { // 每个点的信息 22 int r, x, f; 23 bool operator<( const Item &rhs ) const { 24 return r > rhs.r; // 按照r值排序 25 } 26 }item[MAXN]; 27 28 inline int lowbit( int num ) { return num&(-num); } 29 namespace BIT { // 树状数组相关 30 int c[MAXF] = {0}; 31 void add( int x, int v ) { 32 for( ; x <= MAXF-1; x += lowbit(x) ) 33 c[x] += v; 34 } 35 int query( int x ) { 36 int sum = 0; 37 for( ; x; x -= lowbit(x) ) 38 sum += c[x]; 39 return sum; 40 } 41 int query( int l, int r ) { 42 return query(r) - query(l-1); 43 } 44 void clear( int x ) { 45 for( ; x <= MAXF-1; x += lowbit(x) ) 46 c[x] = 0; 47 } 48 } 49 50 struct Query { 51 int type, x, y, w; 52 // type == 1 表示查询 type == 0 表示修改 53 // w 表示查询对答案的贡献,为1或-1 54 bool operator<( const Query &rhs ) const { 55 if( x == rhs.x ) return type < rhs.type; 56 return x < rhs.x; 57 } 58 }query[MAXN*5], tmp[MAXN*5]; int qidx = 0; 59 60 ll ans = 0; 61 62 void cdq( int L, int R ) { // cdq分治主过程 63 if( R-L <= 1 ) return; 64 int M = (L+R)>>1; cdq(L,M); cdq(M,R); 65 int p = L, q = M, o = L; 66 while( p < M && q < R ) { 67 if( query[p] < query[q] ) { 68 if( query[p].type == 0 ) BIT::add( query[p].y, 1 ); 69 tmp[o++] = query[p++]; 70 } else { 71 if( query[q].type == 1 ) ans += BIT::query( query[q].y ) * query[q].w; 72 tmp[o++] = query[q++]; 73 } 74 } 75 while( p < M ) tmp[o++] = query[p++]; 76 while( q < R ) { 77 if( query[q].type == 1 ) ans += BIT::query( query[q].y ) * query[q].w; 78 tmp[o++] = query[q++]; 79 } 80 for( int i = L; i < R; ++i ) { 81 if( query[i].type == 0 ) BIT::clear( query[i].y ); 82 query[i] = tmp[i]; 83 } 84 } 85 86 int main() { 87 scanf( "%d%d", &n, &k ); 88 for( int i = 0; i < n; ++i ) 89 scanf( "%d%d%d", &item[i].x, &item[i].r, &item[i].f ); 90 sort( item, item+n ); 91 for( int i = 0; i < n; ++i ) { 92 Item &it = item[i]; // 转化为平面上的添加和查询问题 93 int x1 = it.x-it.r, y1 = max( it.f-k, 1 ); 94 int x2 = it.x+it.r, y2 = it.f+k; 95 query[qidx++] = (Query){ 1, x1-1, y1-1, 1 }; 96 query[qidx++] = (Query){ 1, x1-1, y2, -1 }; 97 query[qidx++] = (Query){ 1, x2, y1-1, -1 }; 98 query[qidx++] = (Query){ 1, x2, y2, 1 }; 99 query[qidx++] = (Query){ 0, it.x, it.f, 0 }; // 修改的w值没有意义 100 } 101 cdq(0,qidx); 102 cout << ans << endl; 103 return 0; 104 }