题目大意
题目描述
给定平面上 n n n 个圆形区域,第 i i i 个圆的圆心坐标为 ( p i , q i ) (p_i, q_i) ( p i , q i ) ,半径为 r i r_i r i (保证 r i ≤ 30 r_i \le 30 r i ≤ 3 0 )。
给定 m m m 次询问,每次询问给出一个四边平行于坐标轴的矩形区域,指定了其左下角顶点坐标 ( x i , 1 , y i , 1 ) (x_{i,1}, y_{i,1}) ( x i , 1 , y i , 1 ) 和右上角顶点坐标 ( x i , 2 , y i , 2 ) (x_{i,2}, y_{i,2}) ( x i , 2 , y i , 2 ) 。对于每次询问,需要计算出在给定的 n n n 个圆中,有多少个圆与该矩形区域存在至少一个公共点。
数据范围
测试用例数量 t t t 满足 1 ≤ t ≤ 1000 1 \le t \le 1000 1 ≤ t ≤ 1 0 0 0 。
每次测试中的圆的个数 n n n 满足 1 ≤ n ≤ 1.5 × 1 0 6 1 \le n \le 1.5 \times 10^6 1 ≤ n ≤ 1 . 5 × 1 0 6 ,询问的个数 m m m 满足 1 ≤ m ≤ 1 0 4 1 \le m \le 10^4 1 ≤ m ≤ 1 0 4 。
对于全部测试用例,保证 ∑ n ≤ 3 × 1 0 6 \sum n \le 3 \times 10^6 ∑ n ≤ 3 × 1 0 6 ,∑ m ≤ 2 × 1 0 4 \sum m \le 2 \times 10^4 ∑ m ≤ 2 × 1 0 4 。
坐标与半径范围:1 ≤ p i , q i ≤ 1 0 9 1 \le p_i, q_i \le 10^9 1 ≤ p i , q i ≤ 1 0 9 ,1 ≤ r i ≤ 30 1 \le r_i \le 30 1 ≤ r i ≤ 3 0 ,1 ≤ x i , 1 ≤ x i , 2 ≤ 1 0 9 1 \le x_{i,1} \le x_{i,2} \le 10^9 1 ≤ x i , 1 ≤ x i , 2 ≤ 1 0 9 ,1 ≤ y i , 1 ≤ y i , 2 ≤ 1 0 9 1 \le y_{i,1} \le y_{i,2} \le 10^9 1 ≤ y i , 1 ≤ y i , 2 ≤ 1 0 9 。
样例输入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 2 4 1 1 1 1 7 1 2 5 3 2 1 2 1 2 2 6 4 8 3 5 6 4 7 7 4 3 3 2 1 8 5 10 1 2 1 8 1 7 2 2 5 4 5 9 1 10 5 3 1 6 3 5 1 9 5
样例输出
样例解释
在第一组测试数据中,共有 4 4 4 个圆和 1 1 1 次询问。询问给定的矩形左下角坐标为 ( 2 , 2 ) (2, 2) ( 2 , 2 ) ,右上角坐标为 ( 6 , 4 ) (6, 4) ( 6 , 4 ) 。经过判断,该矩形与输入的第 2 2 2 个圆、第 3 3 3 个圆以及第 4 4 4 个圆(总计三个圆形)存在公共点,因此该次询问的答案为 3 3 3 。第二组测试数据同理,分别输出对应矩形内包含公共点的圆的数量。
思路讲解
这道题目应该是一道扫描线+树状数组,应该是不需要用到这个 K-D 树这种比较高级的东西的。
呃,那我们怎么做这道题目呢?首先就是用这个数据结构,快速求解出大矩形中的这个点,所谓的大矩形,就是矩形和圆的 minkowski 和(这个 minkowski 和是一个圆角矩形),当然,圆角矩形让我们用数据结构处理实在是,我们忽略其圆角,得到的就是所谓的“大矩形”(即图中黑色框框住的部分)。
小羊-2025-12-04-M. Ahmad’s Dish(圆和正多边形的这个 minkowski 和)(minkowski 和的交换不变性)
2025 United Kingdom and Ireland Programming Contest (UKIEPC 2025) 2025 英国 ICPC——B. Brickwork(判断砖块是否重叠)(扫描线的本质就是遍历一个出一个)(维护一个当前生效的集合)(把二维的问题变为一维的问题来做)
okay,我们上面的这个对扫描线总结的比较好。扫描线的重点就是维护一个当前生效的这个集合。按照一顺序排序后,那么我们其实就是要维护一个目前生效的圆的集合 。
首先呢,就是这道题目,因为半径比较少 ,我们不妨把这个不同半径的圆分开处理 。
然后因为我们目前把不同的半径分开处理,我们目前可以假设,所有的圆的这个半径都是相同的。我们就是像上面一样,把这个矩形扩大这个半径(👆想上面那个黑色的矩形一样)。
然后我们把矩形先按照 x 的右端点排序,再按照 x 的左端点排序 。不过,我们发现,无论你怎么排序这个矩形和这个点(也就是这个圆心嘛),都很难维护一个严格生效的这个点的集合,主要也是矩形的是一个这个区间范围,无论怎么排都比较麻烦。
不过我们采用差分思想?其实就是如下方法:
左查询 :在 X = X m i n − 1 X = X_{min} - 1 X = X m i n − 1 的时刻,查询树状数组中 [ Y m i n , Y m a x ] [Y_{min}, Y_{max}] [ Y m i n , Y m a x ] 区间的点数,记为 A A A ,从答案中减去 A A A 。(记录权重为 − 1 -1 − 1 )
右查询 :在 X = X m a x X = X_{max} X = X m a x 的时刻,查询树状数组中 [ Y m i n , Y m a x ] [Y_{min}, Y_{max}] [ Y m i n , Y m a x ] 区间的点数,记为 B B B ,加到答案中。(记录权重为 + 1 +1 + 1 )
采用如上👆方法,我们只需要维护一个 [ − I N F , i d x ] [-INF,idx] [ − I N F , i d x ] 的生效点集合就可以了,可以说是非常轻松惬意了,我们也不需要删除。
然后我们要进行这个几何点的这个扣除:
只要像上面👆一样,不断的枚举这个 dx,就可以排除掉所有的这个不合法的点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 struct Solve { ll N, M; Compress_<ll> li_y; vector<vector<X_y> > r_xy_ls; vector<Event> events_backup; vector<int > Ans; vector<Event> gen_event (ll r) { vector<Event> events; for (auto [x,y]: r_xy_ls[r]) { events.push_back ({x, y, Insert}); } for (auto e: events_backup) { e.y1 -= r; e.y2 += r; if (e.type == Left) { e.x -= r; } else if (e.type == Right) { e.x += r; } events.push_back (e); } sort (all (events)); return events; } void solve_single_r (ll r) { BIT tr (li_y.size()) ; sort (all (r_xy_ls[r])); auto events = gen_event (r); for (auto &e: events) { if (e.type == Insert) { tr.add (li_y.get_i (e.y1), 1 ); } else if (e.type == Left) { Ans[e.id] -= tr.query (li_y.get_i (e.y1), li_y.get_i (e.y2)); Ans[e.id] -= redundant_circle_cnt (r,e.x+r,e.y1+r,-1 ,-1 ); Ans[e.id] -= redundant_circle_cnt (r,e.x+r,e.y2-r,-1 ,1 ); } else { Ans[e.id] += tr.query (li_y.get_i (e.y1), li_y.get_i (e.y2)); Ans[e.id] -= redundant_circle_cnt (r,e.x-r,e.y1+r,1 ,-1 ); Ans[e.id] -= redundant_circle_cnt (r,e.x-r,e.y2-r,1 ,1 ); } } } ll query_r_x_l_r_cnt (ll r, ll x, ll ly, ll ry) { if (ly > ry) { return 0 ; } auto &xy_ls = r_xy_ls[r]; auto itl = lower_bound (all (xy_ls), X_y{x, ly}); if (itl == xy_ls.end ()) { return 0 ; } if (itl->x != x) { return 0 ; } if (itl->y > ry) { return 0 ; } auto itr = upper_bound (all (xy_ls), X_y{x, ry}); ll num = itr - itl; return num; } ll redundant_circle_cnt (ll r, ll x, ll y, ll dirx, ll diry) { ll res = 0 ; for (int dx = 1 ; dx <= r; ++dx) { ll tox = x + dirx * dx; ll disy = sqrtl (r * r - dx * dx); ll toy = y + diry * disy; ll ly = min (toy + diry, y + diry * r); ll ry=max (toy + diry, y + diry * r); res += query_r_x_l_r_cnt (r, tox, ly, ry); } return res; } Solve () : r_xy_ls (32 ) { cin >> N >> M; li_y.reserve (N + M); for (int i = 1 ; i <= N; ++i) { ll x, y, d; cin >> x >> y >> d; r_xy_ls[d].push_back ({x, y}); li_y.add (y); } events_backup.reserve (N + 30 * M); Ans.resize (M); for (int i = 0 ; i < M; ++i) { ll x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2; for (int r = 0 ; r <= 30 ; ++r) { li_y.add (y1 - r, y2 + r); } events_backup.push_back ({x1, y1, Left, i, y2}); events_backup.push_back ({x2, y1, Right, i, y2}); } sort (all (events_backup)); li_y.init (); for (int i = 1 ; i <= 30 ; ++i) { solve_single_r (i); } for (int i = 0 ; i < M; ++i) { cout << Ans[i] << "\n" ; } } };
AC代码
AC
https://acm.hdu.edu.cn/contest/view-code?cid=1198&rid=19570
源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 #include <bits/stdc++.h> #define all(vec) vec.begin(),vec.end() #define lson(o) (o<<1) #define rson(o) (o<<1|1) #define SZ(a) ((long long) a.size()) #define debug(var) cerr << #var <<" = [" <<var<<"]" <<"\n" ; #define debug1d(a) \ cerr << #a << " = [" ; \ for (int i = 0; i < (int)(a).size(); i++) \ cerr << (i ? ", " : "" ) << a[i]; \ cerr << "]\n" ; #define debug2d(a) \ cerr << #a << " = [\n" ; \ for (int i = 0; i < (int)(a).size(); i++) \ { \ cerr << " [" ; \ for (int j = 0; j < (int)(a[i]).size(); j++) \ cerr << (j ? ", " : "" ) << a[i][j]; \ cerr << "]\n" ; \ } \ cerr << "]\n" ; #define cend cerr<<"\n-----------\n" #define fsp(x) fixed<<setprecision(x) using namespace std;using ll = long long ;using ull = unsigned long long ;using DB = double ;using i128 = __int128;using CD = complex<double >;static constexpr ll MAXN = (ll) 1e6 + 10 , INF = (1ll << 61 ) - 1 ;static constexpr ll mod = 998244353 ; static constexpr double eps = 1e-8 ;const double PI = acos (-1.0 );ll lT, testcase; template <typename T>struct Compress_ { int n, shift = 1 ; vector<T> alls; Compress_ () { } Compress_ (ll n_) { alls.reserve (n_); } Compress_ (vector<T> in) : alls (in) { init (); } void add (T x) { alls.emplace_back (x); } template <typename ... Args> void add (T x, Args... args) { add (x), add (args...); } void reserve (ll n_) { alls.reserve (n_); } void init () { alls.emplace_back (numeric_limits<T>::max ()); sort (alls.begin (), alls.end ()); alls.resize (unique (alls.begin (), alls.end ()) - alls.begin ()); this ->n = alls.size (); } int size () { return n; } int get_i (T x) { return upper_bound (alls.begin (), alls.end (), x) - alls.begin () + shift; } T operator [](int idx) { assert (idx - shift < n); return idx - shift < n ? alls[idx - shift] : -1 ; } bool contains (T x) { return binary_search (alls.begin (), alls.end (), x); } friend auto &operator <<(ostream &o, const auto &j) { cout << "{" ; for (auto it: j.alls) { o << it << " " ; } return o << "}" ; } }; struct BIT { vector<ll> tr; int n; inline int lowbit (int x) { return x & (-x); } BIT (int ln) { n = ln; tr.resize (n + 5 , 0 ); } BIT () { } inline void add (int p, ll x) { if (p == 0 ) return ; while (p <= n) { tr[p] += x; p += lowbit (p); } } inline ll query (int l, int r) { ll lres = 0 , rres = 0 ; l -= 1 ; while (l > 0 ) { lres += tr[l]; l -= lowbit (l); } while (r > 0 ) { rres += tr[r]; r -= lowbit (r); } return rres - lres; } inline int findkth (int k) { int x = 0 , sum = 0 ; for (int i = __lg(n); ~i; --i) { x += 1 << i; if (x >= n || sum + tr[x] >= k) { x -= 1 << i; } else { sum += tr[x]; } } return x + 1 ; } inline int kthspace (int k) { int cx = 0 ; for (int i = 1 << 20 ; i > 0 ; i >>= 1 ) { if (cx + i <= n && lowbit (cx + i) - tr[cx + i] < k) { k -= lowbit (cx + i) - tr[cx + i]; cx += i; } } return cx + 1 ; } }; struct X_y { ll x; ll y; bool operator <(const X_y &o) const { if (x != o.x) return x < o.x; return y < o.y; } bool operator ==(const X_y &o) const { return x == o.x && y == o.y; } }; enum my_enum { Left, Insert, Right, }; struct Event { ll x, y1; my_enum type; int id; ll y2; bool operator <(const Event &o) const { if (x != o.x) return x < o.x; if (type != o.type) return type < o.type; return y1 < o.y1; } }; struct Solve { ll N, M; Compress_<ll> li_y; vector<vector<X_y> > r_xy_ls; vector<Event> events_backup; vector<int > Ans; vector<Event> gen_event (ll r) { vector<Event> events; for (auto [x,y]: r_xy_ls[r]) { events.push_back ({x, y, Insert}); } for (auto e: events_backup) { e.y1 -= r; e.y2 += r; if (e.type == Left) { e.x -= r; } else if (e.type == Right) { e.x += r; } events.push_back (e); } sort (all (events)); return events; } void solve_single_r (ll r) { BIT tr (li_y.size()) ; sort (all (r_xy_ls[r])); auto events = gen_event (r); for (auto &e: events) { if (e.type == Insert) { tr.add (li_y.get_i (e.y1), 1 ); } else if (e.type == Left) { Ans[e.id] -= tr.query (li_y.get_i (e.y1), li_y.get_i (e.y2)); Ans[e.id] -= redundant_circle_cnt (r,e.x+r,e.y1+r,-1 ,-1 ); Ans[e.id] -= redundant_circle_cnt (r,e.x+r,e.y2-r,-1 ,1 ); } else { Ans[e.id] += tr.query (li_y.get_i (e.y1), li_y.get_i (e.y2)); Ans[e.id] -= redundant_circle_cnt (r,e.x-r,e.y1+r,1 ,-1 ); Ans[e.id] -= redundant_circle_cnt (r,e.x-r,e.y2-r,1 ,1 ); } } } ll query_r_x_l_r_cnt (ll r, ll x, ll ly, ll ry) { if (ly > ry) { return 0 ; } auto &xy_ls = r_xy_ls[r]; auto itl = lower_bound (all (xy_ls), X_y{x, ly}); if (itl == xy_ls.end ()) { return 0 ; } if (itl->x != x) { return 0 ; } if (itl->y > ry) { return 0 ; } auto itr = upper_bound (all (xy_ls), X_y{x, ry}); ll num = itr - itl; return num; } ll redundant_circle_cnt (ll r, ll x, ll y, ll dirx, ll diry) { ll res = 0 ; for (int dx = 1 ; dx <= r; ++dx) { ll tox = x + dirx * dx; ll disy = sqrtl (r * r - dx * dx); ll toy = y + diry * disy; ll ly = min (toy + diry, y + diry * r); ll ry=max (toy + diry, y + diry * r); res += query_r_x_l_r_cnt (r, tox, ly, ry); } return res; } Solve () : r_xy_ls (32 ) { cin >> N >> M; li_y.reserve (N + M); for (int i = 1 ; i <= N; ++i) { ll x, y, d; cin >> x >> y >> d; r_xy_ls[d].push_back ({x, y}); li_y.add (y); } events_backup.reserve (N + 30 * M); Ans.resize (M); for (int i = 0 ; i < M; ++i) { ll x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2; for (int r = 0 ; r <= 30 ; ++r) { li_y.add (y1 - r, y2 + r); } events_backup.push_back ({x1, y1, Left, i, y2}); events_backup.push_back ({x2, y1, Right, i, y2}); } sort (all (events_backup)); li_y.init (); for (int i = 1 ; i <= 30 ; ++i) { solve_single_r (i); } for (int i = 0 ; i < M; ++i) { cout << Ans[i] << "\n" ; } } }; signed main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); cout.tie (nullptr ); #ifdef LOCAL cout.setf (ios::unitbuf); #endif cin >> lT; for (testcase = 1 ; testcase <= lT; ++testcase) Solve solve; return 0 ; }
心路历程(WA,TLE,MLE……)
也是成功一次 AC 了,当然,在写一些模块的时候,ai 也帮助我进行了一些东西。