首页 > 其他分享 >[JOISC 2021 Day2] 道路の建設案 (Road Construction)

[JOISC 2021 Day2] 道路の建設案 (Road Construction)

时间:2023-10-19 11:14:38浏览次数:25  
标签:建設案 int mi Day2 mx tr JOISC ri define

[JOISC 2021 Day2] 道路の建設案 (Road Construction)

题意

给定图上 \(n\) 个点,求前 \(k\) 小曼哈顿距离点对距离。

题解

很好的一道题。

首先有一个 \(O(nlog^2n)\) 的做法,个人感觉还是很有启发性的。
一般对于第 \(k\) 小的处理方法是二分,二分第 \(k\) 小的距离是多少。
然后统计大于等于这个距离的点对数,注意到曼哈顿距离不太好满足,考虑转化为切比雪夫距离。
这样两维就分开了,考虑先将 \(x\) 超过距离的计算上,然后只对 \(x\) 不超过的计算 \(y\) 的贡献,容易发现这样可以用线段树维护。
但是每求一个都要二分,最坏的时间复杂度会达到 \(O(nklog^2n)\),这样很不牛。
为什么不牛,因为我们只关注了点对的数量,无法具体的对应到点对上,但一般题这样做的原因是因为 \(k\) 比较大。
但是这题 \(k\) 和 \(n\) 是同阶的,我们可以发现我们其实可以暴力找到所有合法点对。
显然可以直接用 multiset 直接暴力数,很好做。
所以我们找到第 \(k\) 小的路径长度 \(mid\) 之后,再找一下小于等于 \(mid-1\) 的所有点对就好了,时间复杂度 \(O(nlog^2n)\)。

然后有一个 \(O(nlogn)\) 的做法,还是很牛的。
首先对于这种 \(k\) 比较小的问题,一般都是使用类似超级钢琴的做法。
对于每个点找到左边最近的点对,然后丢到堆里面,然后取出来之后扩展找第 \(2\) 小,第 \(3\) 小等。
考虑怎么找左边最近的点,一般的做法是扫描线,左边的点的 \(x\) 都是被减的,然后将 \(y\) 放到线段树上维护一个 \(y-x,-y-x\) 即可。
然后由于每一个点到后面都要找次小值,而且不能确定顺序,所以要可持久化扫描线上线段树的版本。
考虑如何找次小值,删除最小值即可,但是因为可持久化了,所以为了防止影响其它版本,所以仍然要新建版本。
为了好写可以将值的离散化成互不相同的值,因为在线段树上同一个叶节点维护的东西可能会比较麻烦,时间复杂度 \(O(nlogn)\)。

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
#define rg register
#define pc putchar
#define gc getchar
#define pf printf
#define space pc(' ')
#define enter pc('\n')
#define me(x,y) memset(x,y,sizeof(x))
#define pb push_back
#define FOR(i,k,t,p) for(rg int i(k) ; i <= t ; i += p)
#define ROF(i,k,t,p) for(rg int i(k) ; i >= t ; i -= p)
using namespace std ;
bool s_gnd ;
inline void read(){}
template<typename T,typename ...T_>
inline void read(T &x,T_&...p)
{
    x = 0 ;rg int f(0) ; rg char c(gc()) ;
    while(!isdigit(c)) f |= (c=='-'),c = gc() ;
    while(isdigit(c)) x = (x<<1)+(x<<3)+(c^48),c = gc() ;
    x = (f?-x:x) ;
    read(p...);
}
int stk[30],tp ;
inline void print(){}
template<typename T,typename ...T_>
inline void print(T x,T_...p)
{
    if(x < 0) pc('-'),x = -x ;
    do stk[++tp] = x%10,x /= 10 ; while(x) ;
    while(tp) pc(stk[tp--]^48) ; space ;
    print(p...) ;
}
bool S_GND ;
const int N = 5e5+5 ;
const int INF = 1e18+7 ;
int n,k,top ;
pii b[N] ;
int rt[N] ;
unordered_map<int,int>rk ;
struct Node{int x,y ;}a[N] ;
struct RP{int x,y,id ;}c[N] ;
struct Pair
{
    int u,v,w ;
    bool operator < (const Pair &A) const
    {
        return w > A.w ;
    }
} ;
priority_queue<Pair>q ;
int cmp(RP x,RP y) {return x.x < y.x ;}
struct Seg{int l,r ; pii mx,mi ;}tr[N*60] ;
#define ls(x) tr[x].l
#define rs(x) tr[x].r
#define mi(x) tr[x].mi
#define mx(x) tr[x].mx
#define mid (le+ri>>1)
void update(int x)
{
    mx(x) = max(mx(ls(x)),mx(rs(x))) ;
    mi(x) = min(mi(ls(x)),mi(rs(x))) ;
}
void Build(int &x,int le = 1,int ri = n)
{
    x = ++top ; if(le == ri) {mi(x) = {INF,0},mx(x) = {-INF,0} ; return ;}
    Build(ls(x),le,mid),Build(rs(x),mid+1,ri),update(x) ;
}
void Insert(int &x,int y,int k,int xi,int yi,int id,int le = 1,int ri = n)
{
    x = ++top,tr[x] = tr[y] ; if(le == ri) {mx(x) = {xi+yi,id},mi(x) = {yi-xi,id} ; return ;}
    (k <= mid) ? Insert(ls(x),ls(y),k,xi,yi,id,le,mid) : Insert(rs(x),rs(y),k,xi,yi,id,mid+1,ri) ; update(x) ;
}
void Delete(int &x,int y,int k,int le = 1,int ri = n)
{
    x = ++top,tr[x] = tr[y] ; if(le == ri) {mx(x) = {-INF,0},mi(x) = {INF,0} ; return ;} 
    (k <= mid) ? Delete(ls(x),ls(y),k,le,mid) : Delete(rs(x),rs(y),k,mid+1,ri) ; update(x) ;
}
pii Query_mi(int x,int L,int R,int le = 1,int ri = n)
{
    if(le >= L && ri <= R) return mi(x) ; pii res = {INF,0} ;
    if(L <= mid) res = Query_mi(ls(x),L,R,le,mid) ; if(mid < R) res = min(res,Query_mi(rs(x),L,R,mid+1,ri)) ; return res ;
}
pii Query_mx(int x,int L,int R,int le = 1,int ri = n)
{
    if(le >= L && ri <= R) return mx(x) ; pii res = {-INF,0} ;
    if(L <= mid) res = Query_mx(ls(x),L,R,le,mid) ; if(mid < R) res = max(res,Query_mx(rs(x),L,R,mid+1,ri)) ; return res ;
}
signed main()
{
//cerr<<(double)(&s_gnd-&S_GND)/1024.0/1024.0 ;
//	freopen(".in","r",stdin) ;
//	freopen(".out","w",stdout) ;
    read(n,k) ;
    FOR(i,1,n,1) read(a[i].x,a[i].y),c[i].id = i ;
    FOR(i,1,n,1) b[i] = {a[i].x,i} ; sort(b+1,b+1+n) ;
    FOR(i,1,n,1) c[b[i].second].x = i ;
    FOR(i,1,n,1) b[i] = {a[i].y,i} ; sort(b+1,b+1+n) ;
    FOR(i,1,n,1) c[b[i].second].y = i ;
    sort(c+1,c+1+n,cmp),Build(rt[0],1,n) ; // cerr<<"!" ;
    // FOR(i,1,n,1) print(c[i].id) ; enter ;
    FOR(i,1,n,1)
    {
        pii jd = {INF,INF} ;
        pii pos = Query_mx(rt[i-1],1,c[i].y) ;
        if(pos.second) jd = min(jd,{a[c[i].id].x+a[c[i].id].y-pos.first,pos.second}) ; //print(a[c[i].id].x+a[c[i].id].y-pos.first,pos.first,pos.second),enter ;
        pos = Query_mi(rt[i-1],c[i].y,n) ;
        if(pos.second) jd = min(jd,{a[c[i].id].x+pos.first-a[c[i].id].y,pos.second}) ; //print(a[c[i].id].x+pos.first-a[c[i].id].y,pos.first,pos.second),enter ;
        if(jd.second) q.push({jd.second,i,jd.first}) ; Insert(rt[i],rt[i-1],c[i].y,a[c[i].id].x,a[c[i].id].y,i) ;
    }
    FOR(p,1,k,1)
    {
        auto [x,y,w] = q.top() ; q.pop() ;
        print(w),enter ; Delete(rt[y-1],rt[y-1],c[x].y) ; int i = y ;
        pii jd = {INF,INF},pos = Query_mx(rt[i-1],1,c[i].y) ;
        if(pos.second) jd = min(jd,{a[c[i].id].x+a[c[i].id].y-pos.first,pos.second}) ;
        pos = Query_mi(rt[i-1],c[i].y,n) ;
        if(pos.second) jd = min(jd,{a[c[i].id].x+pos.first-a[c[i].id].y,pos.second}) ;
        if(jd.second) q.push({jd.second,i,jd.first}) ;
    }
    return 0 ;
}

标签:建設案,int,mi,Day2,mx,tr,JOISC,ri,define
From: https://www.cnblogs.com/snow-panther/p/17774261.html

相关文章

  • DataWhale DAY2 基础语法1
    DataWhaleDAY2基础语法1今天主要是一点入门语法,import什么的,所以重点不放在上面。语法部分专门开一章:https://www.cnblogs.com/hewo/p/17635277.html关于浮点数精度问题,倒是有点意思。以前学c++的时候,尤其是计算几何的时候,经常设一个极小常量来比较,现在明白本质上是进......
  • Day2 数组训练
    Day2数组的一些基本练习前一阵子生病了,把这几天落下来的内容慢慢补第一题有序数组的平方Lc977给你一个按非递减顺序排序的整数数组nums,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。//使用双指针的思想完成此题,一开始我想的是直接暴力解,这有什么难的......
  • pythonDay2
    变量1.引用计数增加 2;引用计数减少代码规范快捷键:ctrl+alt+l3.变量名的命名规则  is(id)和 ==(值) 4.常量5.基本数据类型   其他 6.列表  取最后一个子列表:print(l[-1])  7.字典类型: 8.布尔Bool类型(if判断中会用到) ......
  • C++基础认识(新手村)day2
    引用的使用场景1.引用作为函数参数//1.引用作为函数参数voidfun1(int&a,int&b){ intsum=a+b; cout<<"sum="<<sum<<endl;}voidtest1(){ inta=10; intb=20; fun1(a,b);}2.引用作为函数的返回值//2.引用作为函数的返回值int&fun2(){ intb......
  • Day2 前缀和 差分 双指针
    前缀和LuoguP2004领地选择二维前缀和板题,注意开longlong#include<iostream>#include<cstdio>#include<algorithm>usingnamespacestd;intn,m,c,x,y;longlongans,a[1005][1005],s[1005][1005];intmain(){ scanf("%d%d%d",&n......
  • noip赛前20天冲刺集训 day2 ###寻找有向图中的最小疲惫路径###
    T1###寻找有向图中的最小疲惫路径###题目描述有一张n个点m条边的有向图,每条边上有一个正整数边权,你要顺着图上的有向边从1号点走到n号点。假设你经过的边边权依次为(w_1,w_2,\dots,w_t),则你的疲惫程度为\[\f(w)=\max_{i=1}^{t}w_i\timesi\,.\]你需要找到最......
  • [UOJ618]【JOISC2021】聚会 2
    #618.【JOISC2021】聚会2就是相当于选中的点在整棵树上的重心首先,当\(i\)为奇数时,答案为\(1\)当\(i\)为偶数时,可以将选中的点分为两个子树,分别记其根节点为\(x\)和\(y\)那么可以发现,所以合法的\(x\)和\(y\)构成一个连通块,那么当前答案就是连通块的直径,且随着\(i\)的增大,连通......
  • 谷粒商城-day2
    1人人开源搭建后台管理系统                 3、部署代码生成器                                     ......
  • 算法训练day29 LeetCode 39.40.131
    算法训练day29LeetCode39.40.13139.组合总和题目39.组合总和-力扣(LeetCode)题解代码随想录(programmercarl.com)classSolution{private:vector<vector<int>>result;vector<int>path;voidbacktracking(vector<int>&candidates,......
  • Day2 of my diary of C program learning
    somebugImeet1. if(a>b,a<c)shouldbeif(a>b&&a<c)2. scanf forget'&''3. exp1?exp2:exp3insteadofexp1?exp2;exp3。while for(;;)4. Asforexp1?exp2:exp3。exp==min=a,max=b0;exp==(min=a,max=b)1;两个代码的区别#include<st......