【题解】「JOISC 2021 Day2」道路建设

提供一个二分 + set 的做法。

首先二分一个距离 m i d mid mid ,考虑距离 ≤ m i d \leq mid mid 的点对个数,此时合法点对的个数 ≤ K \leq K K 。得到答案后,再将所有距离 ≤ m i d − 1 \leq mid-1 mid1 的方案都构造出来,最后输出若干个 m i d mid mid 占位。

我们来考虑直接做这道题,类似于天使玩偶,可以分两个点的位置进行讨论,内部用树状数组求答案,是一个三维偏序的经典问题 (天使玩偶),时间复杂度 O ( n l o g 3 n ) O(nlog^3n) O(nlog3n)

这里有一个有用的剪枝,就是当找到 K K K 个点对后,可以直接返回 f a l s e false false ,但是 c d q cdq cdq 的复杂度仍然是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

下面提供一个 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n) 的做法,即不会找超过 K K K 个点对,内部用一个带 l o g log log s e t set set 维护。

事实上这道题 可以转化为二维偏序。这是一个非常重要的思想,因为求没有重复元素的三维点对时也可以用二维偏序来计算。

考虑定义 a i = x i + y i a_i=x_i+y_i ai=xi+yi b i = x i − y i b_i=x_i-y_i bi=xiyi,点对的新距离为 m a x ( a b s ( a i − a j ) , a b s ( b i − b j ) ) max(abs(a_i-a_j),abs(b_i-b_j)) max(abs(aiaj),abs(bibj)) 。这其实是一个等价转换。然而不同的是,我们去掉了 x i < x j x_i<x_j xi<xj 的限制。

那么我们只需要按照 a i a_i ai 从小到大排序,后面的减去前面的就可以消去绝对值,而后面那个可以直接在 ( b i − K , b i + K ) (b_i-K,b_i+K) (biK,bi+K) 的范围内查找,即在 s e t set set 里进行二分查找。

这个方法时间复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n) ,巧妙运用了绝对值和 m a x max max 函数的性质。

#include <bits/stdc++.h> #define pii pair<int,int> #define mp(x,y) make_pair(x,y) #define pb push_back #define fi first #define se second #define int long long using namespace std; const int mx = 1e6 + 5; inline int read() { int X = 0; bool flag = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') flag = 0; ch = getchar(); } while (ch >= '0' && ch <= '9') { X = (X << 1) + (X << 3) + ch - '0'; ch = getchar(); } if (flag) return X; return ~(X - 1); } //二分+三维偏序+set //时间复杂度 O(nlog^3n) //考虑当合法状态较少时,cdq的复杂度很大 //考虑扔掉一定不产生贡献的答案(实际不可行) //考虑二维偏序的做法,启发极大 //时间复杂度 O(nlog^2n+Klog^2n) ,其中 n,K 复杂度是分开算的 int n, K, M, cnt, ans[mx]; pii q[mx], X[mx], Y[mx]; set<pii> s; set<pii>::iterator it; int dist(int x, int y) { return max(abs(q[x].fi - q[y].fi), abs(q[x].se - q[y].se)); } bool check() { cnt = 0; s.clear(); int j = 1; for (int i = 1; i <= n; i++) { while (j < i && q[i].fi - q[j].fi > M) { s.erase(mp(q[j].se, j)); j++; } it = s.lower_bound(mp(q[i].se - M, 0)); for (; it != s.end(); it++) { pii now = (*it); if (now.fi - q[i].se > M) { break; } ans[++cnt] = dist(i, now.se); if (cnt >= K) return 0; } s.insert(mp(q[i].se, i)); } return cnt < K; } signed main() { // freopen("data.in","r",stdin); // freopen("b.in","r",stdin); // freopen("ans.out","w",stdout); n = read(), K = read(); for (int i = 1; i <= n; i++) { int x = read(), y = read(); q[i] = mp(x - y, x + y); } sort(q + 1, q + 1 + n); // M=3; check(); // M=1; if(check()) printf("AC"); // printf("%lld\n",cnt); int l = 0, r = 4e9, res = 0; while (l <= r) { M = (l + r) >> 1; if (check()) l = M + 1, res = M; else r = M - 1; } // // // printf("%lld\n",res); M = res, check(); sort(ans + 1, ans + 1 + cnt); for (int i = 1; i <= cnt; i++) { printf("%lld\n", ans[i]); } for (int i = 1; i <= K - cnt; i++) { printf("%lld\n", M + 1); } // printf("%lld",res); // for(int i=3;i<=7;i++) { // M=i; // if(check()) printf("%d\n",i); // } }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530335.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(16)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示