离散化+线段树+二分查找
牛客 Forsaken的三维数点
题目描述
Forsaken现在在一个三维空间中,空间中每个点都可以用(x,y,z)(x,y,z)表示。突然,三维空间的主人出现了,如果Forsaken想要继续在三维空间中呆下去,他就必须回答三维空间主人的问题。
主人会在空间中坐标为(x,y,z)(x,y,z)处加一点能量值,当他加了一定的次数之后,他会问Forsaken一个问题:如果坐标(0,0,0)(0,0,0)为球心,那么至少需要多大的半径才能使得球内的能量值总和大于或者等于kk,在这里,半径为00也是可以的。这对于Forsaken来说实在是太难了,因此他把这个问题交给了你。
输入描述:
第一行一个nn表示操作的次数。
接下来每行首先一个整数opop表示操作的种类。
如果op = 1op=1,接下来33个整数x,y,zx,y,z表示能量值增加的坐标。
如果op =2op=2,接下来一个整数kk表示要求的能量值总和。
输出描述:
对于每个op=2op=2的操作,输出一个整数表示球的半径。(数据保证至少有一个22操作)
如果没有满足答案的半径,输出-1−1。
备注:
1 ≤n≤2e5
1 ≤op≤2
-1e5 ≤x,y,z≤1e5
0≤k≤2e5
首先降维,因为题目问题的关键是举例,所以通过sqrt(x^2 + y ^2 + z ^2)降为一维。
解法一:离散化+线段树+二分查找
题目坐标最大是1e5,平方后是1e10,直接建树无法实现。但是观察数据发现,最多有1e5个输入,也就是说每次做多有1e5个点,因此想到离散化。
离散化的前提是知道全部的输入,所以想到把问题离线处理。
这样,把所有的距离输入后,从小到大排一遍序,便离散处理完了。
如何建树?
线段树中每一个中间节点存储的是它左右孩子节点的能量值的和。
更新操作时,只更新对应的点。
对于题目问题的查询操作:
每一个节点可以分为两部分,前半部分 和 后半部分。
假设查询操作的值为k,
如果k大于结点的值,则直接返回-1.
如果k小于等于前半部分的能量值,则进入前半部分继续查找k。否则,进入后半部分查询 k - sum[i << 1] 。
AC代码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <math.h> 5 #include <algorithm> 6 #include <queue> 7 #include <map> 8 #include <stack> 9 #include <deque> 10 #include <iostream> 11 using namespace std; 12 typedef long long LL; 13 const LL N = 200009; 14 15 struct con 16 { 17 LL l, r; 18 } con[4 * N]; 19 LL sum[4 * N]; 20 21 LL tri[200009]; 22 LL distill[200009]; 23 struct q 24 { 25 LL k; 26 LL i; 27 } q[200009]; 28 29 map<LL, LL> qq; 30 31 void pushUp(LL i) 32 { 33 sum[i] = sum[i << 1] + sum[i << 1 | 1]; 34 } 35 36 void build(LL i, LL l, LL r) 37 { 38 con[i].l = l; 39 con[i].r = r; 40 if (con[i].l == con[i].r) 41 { 42 sum[i] = 0; 43 return; 44 } 45 LL mid = (con[i].l + con[i].r) >> 1; 46 build(i << 1, l, mid); 47 build(i << 1 | 1, mid + 1, r); 48 49 pushUp(i); 50 } 51 52 void update(LL i, LL l, LL r) 53 { 54 // cout << " ** " << i << " " << con[i].l << " " << con[i].r << " " << l << " " << r << endl; 55 if (con[i].l == l && con[i].r == r) 56 { 57 sum[i]++; 58 return; 59 } 60 LL mid = (con[i].l + con[i].r) >> 1; 61 62 if (l > mid) 63 { 64 update(i << 1 | 1, l, r); 65 } 66 else if (r <= mid) 67 { 68 update(i << 1, l, r); 69 } 70 else 71 { 72 update(i << 1, l, mid); 73 update(i << 1 | 1, mid + 1, r); 74 } 75 76 pushUp(i); 77 } 78 79 LL query(LL i, LL k) 80 { 81 //cout << " ?? " << i << " " << con[i].l << " " << con[i].r << " " << sum[i] << " " << k << endl; 82 if (sum[i] < k) 83 { 84 return -1; 85 } 86 87 if (con[i].l == con[i].r) 88 { 89 return con[i].l; 90 } 91 92 if (k <= sum[i << 1]) 93 { 94 return query(i << 1, k); 95 } 96 else 97 { 98 return query(i << 1 | 1, k - sum[i << 1]); 99 } 100 } 101 102 LL dis(LL x, LL y, LL z) 103 { 104 return x * x + y * y + z * z; 105 } 106 107 int main() 108 { 109 LL n, i, j, m, mid, tmp; 110 LL k, x, y, z, cnt1 = 0, cnt2 = 0, cnt3 = 0; 111 112 scanf("%lld", &n); 113 for (i = 1; i <= n; i++) 114 { 115 scanf("%lld", &m); 116 if (m == 1) 117 { 118 scanf("%lld%lld%lld", &x, &y, &z); 119 mid = dis(x, y, z); 120 if (qq[mid] == 0) 121 { 122 distill[++cnt3] = mid; 123 qq[mid] = 1; 124 } 125 126 tri[++cnt1] = mid; 127 } 128 else 129 { 130 scanf("%lld", &q[++cnt2].k); 131 q[cnt2].i = i; 132 tri[++cnt1] = 0; 133 } 134 } 135 sort(distill + 1, distill + 1 + cnt3); 136 for (i = 1; i <= cnt3; i++) 137 { 138 qq[distill[i]] = i; 139 } 140 141 // cout << endl << endl; 142 // for(i = 1; i <= cnt1; i++) 143 // cout << i << " " << tri[i] << endl; 144 // 145 // cout << endl << endl; 146 // for(i = 1; i <= cnt2; i++) 147 // cout << q[i].i << " " << q[i].k << endl; 148 // 149 // cout << endl << endl; 150 // for(i = 1; i <= cnt3; i++) 151 // cout << distill[i] << " " ; 152 // 153 // cout << endl << endl; 154 155 if(cnt3 < 1) 156 { 157 for(i = 1; i <= cnt2; i++) 158 { 159 if(q[i].k == 0) 160 printf("0\n"); 161 else 162 printf("-1\n"); 163 } 164 } 165 else 166 { 167 build(1, 1, cnt3); 168 // cout << "hello " << endl; 169 170 j = 1; 171 for (i = 1; i <= cnt2; i++) 172 { 173 // cout << "world!" << endl; 174 while (j < q[i].i) 175 { 176 // cout << " ** " << qq[tri[j]] << " " << cnt3 << endl; 177 update(1, qq[tri[j]], qq[tri[j]]); 178 j++; 179 } 180 181 if(q[i].k == 0) 182 { 183 printf("0\n"); 184 } 185 else 186 { 187 tmp = query(1, q[i].k); 188 if (tmp == -1) 189 printf("-1\n"); 190 else 191 { 192 LL ans = ceil(sqrt((double)distill[tmp])); 193 printf("%lld\n", ans); 194 } 195 } 196 197 j++; 198 } 199 } 200 201 202 return 0; 203 }
解法二:简单线段树
因为查询的是距离,且距离最多是1e5A,并且题目要求的是向上取整的整数值,所以直接按ceil(sqrt(x^2 + y ^2 + z ^2))距离建树就好了。
AC代码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <math.h> 5 #include <algorithm> 6 #include <queue> 7 #include <map> 8 #include <stack> 9 #include <deque> 10 #include <iostream> 11 using namespace std; 12 typedef long long LL; 13 const LL N = 200009; 14 15 struct con 16 { 17 LL l, r; 18 } con[4 * N]; 19 LL sum[4 * N]; 20 LL len; 21 22 void pushUp(LL i) 23 { 24 sum[i] = sum[i << 1] + sum[i << 1 | 1]; 25 } 26 27 void build(LL i, LL l, LL r) 28 { 29 con[i].l = l; 30 con[i].r = r; 31 if (con[i].l == con[i].r) 32 { 33 sum[i] = 0; 34 return; 35 } 36 LL mid = (con[i].l + con[i].r) >> 1; 37 build(i << 1, l, mid); 38 build(i << 1 | 1, mid + 1, r); 39 40 //pushUp(i); 41 } 42 43 void update(LL i, LL l, LL r) 44 { 45 // cout << " ** " << i << " " << con[i].l << " " << con[i].r << " " << l << " " << r << endl; 46 47 //if (con[i].l == l && con[i].r == r) 48 if (con[i].l == l && con[i].r == r) 49 { 50 sum[i]++; 51 return; 52 } 53 //LL mid = (con[i].l + con[i].r) >> 1; 54 55 LL m = (con[i].l + con[i].r) >> 1; 56 if (r <= m) 57 update(i << 1, l, r); 58 else 59 { 60 if (l > m) 61 update(i << 1 | 1, l, r); 62 else 63 { 64 update(i << 1, l, m); 65 update(i << 1 | 1, m + 1, r); 66 } 67 } 68 /* 69 LL mid = (l + r) >> 1; 70 if (len <= mid) 71 update(i << 1, l, mid); 72 else 73 { 74 update(i << 1 | 1, mid + 1, r); 75 } 76 */ 77 pushUp(i); 78 } 79 80 LL query(LL i, LL k) 81 { 82 // cout << " ?? " << i << " " << con[i].l << " " << con[i].r << " " << sum[i] << " " << k << endl; 83 if (sum[i] < k) 84 { 85 return -1; 86 } 87 88 if (con[i].l == con[i].r) 89 { 90 return con[i].r; 91 } 92 93 if (k <= sum[i << 1]) 94 { 95 return query(i << 1, k); 96 } 97 else 98 { 99 return query(i << 1 | 1, k - sum[i << 1]); 100 } 101 } 102 103 LL dis(LL x, LL y, LL z) 104 { 105 return ceil(sqrt(x * x + y * y + z * z)); 106 } 107 108 int main() 109 { 110 LL n, i, j, m, ans; 111 LL k, x, y, z; 112 113 build(1, 0, N); 114 scanf("%lld", &n); 115 for (i = 1; i <= n; i++) 116 { 117 scanf("%lld", &m); 118 if (m == 1) 119 { 120 scanf("%lld%lld%lld", &x, &y, &z); 121 len = dis(x, y, z); 122 update(1, len, len); 123 } 124 else 125 { 126 scanf("%lld", &k); 127 if (k == 0) 128 ans = 0; 129 else 130 { 131 ans = query(1, k); 132 } 133 printf("%lld\n", ans); 134 } 135 } 136 137 return 0; 138 }