题面传送门

题意:

一条河将大地分为 \(A,B\) 两个部分。两部分均可视为一根数轴。

有 \(n\) 名工人,第 \(i\) 名的家在 \(x_i\) 区域的 \(a_i\) 位置,公司在 \(y_i\) 区域的 \(b_i\) 位置。

现在你可以建立 \(k\) 座桥,在 \(x\) 位置建立一座桥可以连接 \(A\) 区域的 \(x\) 位置和 \(B\) 区域的 \(x\) 位置,桥长为 \(1\) 个单位长度。

设 \(d_i\) 为第 \(i\) 名工人从家到公司走过的最短距离,求 \(D=d_1+d_2+\dots+d_n\) 的最小值。

\(n \in [1,2\times 10^5],k\in\{1,2\},a_i,b_i \leq 10^9,x_i,y_i \in \{'A','B'\}\)。

一开始看错题了,以为 \(k\) 的数据范围也是 \(10^5\)。。。。。。然后就不愿意继续想下去了。

首先如果家和公司在河同一边那方案肯定是唯一的,直接加上 \(|a_i-b_i|\)。

接下来重点考虑家和公司不在河同一边的情况,假设这些工人 \(d_i\) 的和为 \(D'\)

先从 \(k=1\) 入手,假设我们在 \(p\) 位置建了座桥,那么所有家和公司不在河同一边都必须通过这一座桥,即 \(d_i=|a_i-p|+|b_i-p|+1\)。

\(D'=\sum |a_i-p|+|b_i-p|+1\)

最后那个 \(1\) 显然可以直接处理掉,剩余部分就是一个初一弱智数学问题,直接取中位数所有 \(a_i,b_i\) 就可以了。

接下来考虑 \(k=2\) 的情况。假设我们在 \(p,q\) 位置建了桥。

那么 \(d_i\) 就是从这两座桥上通过所需的距离的较小值,即 \(d_i=\min(|a_i-p|+|b_i-p|,|a_i-q|+|b_i-q|)+1\)。

在 \(k=1\) 的情况中,我们之所以能够把 \(a_i,b_i\) 揉在一起取中位数,是因为它们的贡献互不影响。

但在这种情况下就不能直接取中位数了,因为有个 \(\min\)。

不妨设 \(a_i \leq b_i\),设 \(f(x)=|a_i-x|+|b_i-x|\)。简单画个图像,由三部分组成,左边是斜率为 \(-2\) 的射线,中间是一段水平线段,右边是斜率为 \(2\) 的射线。图像的对称轴为 \(x=\dfrac{a_i+b_i}{2}\)(梦回课内)

由于对称轴左右两边完全一样并且对称轴左边 \(y\) 随 \(x\) 的增大单调不降,故有:

  • 若 \(|\dfrac{a_i+b_i}{2}-p|<|\dfrac{a_i+b_i}{2}-q|\),那么 \(f(p)\leq f(q)\)。

有了这个结论,本题就简单多了。

把所有工人按 \(\dfrac{a_i+b_i}{2}\) 从小到大排序,那我们肯定是选择离 \(\dfrac{a_i+b_i}{2}\) 较近的那座桥。

不妨设 \(p<q\),那么所有 \(\dfrac{a_i+b_i}{2}\leq\dfrac{p+q}{2}\) 的工人都会选择 \(p\) 那座桥,剩余的工人会选择 \(q\) 那座桥。

故选择 \(p\) 的工人是原序列的一个前缀,选择 \(q\) 的工人是原序列的一个后缀。

枚举断点 \(i\),\([1,i]\) 的工人选择桥梁 \(p\),\([i+1,n]\) 的工人选择桥梁 \(q\)。

断点两边是互相独立的。这时候我们又可以把绝对值拆开,于是我们又回到了第一问。

于是题目变为如何快速求出每个前缀的中位数,有 DS 味儿了,稍微一想就可以想到平衡树(

但稍微想一想就发现,其实根本不用平衡树。可以用两个堆来维护,建一个大根堆维护前一半的值,再建一个小根堆维护后一半的值。每次新插入一个值,就将它插入到对应的部分中,然后通过微调使两部分平衡。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a)
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
typedef pair<int,int> pii;
typedef long long ll;
const int MAXN=1e5+5;
struct solver{
multiset<int> st1,st2;
ll sum1=0,sum2=0;
void insert(int a){
if(st1.empty()){
st1.insert(a);sum1+=a;
} else {
int x=*st1.rbegin();
if(a<=x) sum1+=a,st1.insert(a);
else sum2+=a,st2.insert(a);
int cnt=(st1.size()+st2.size()+1)/2;
while(st1.size()>cnt){int v=*st1.rbegin();sum1-=v;sum2+=v;st1.erase(st1.find(v));st2.insert(v);}
while(st1.size()<cnt){int v=*st2.begin();sum2-=v;sum1+=v;st2.erase(st2.find(v));st1.insert(v);}
}
}
ll query(){
if(st1.empty()) return 0;
int cnt=(st1.size()+st2.size()+1)/2,x=*st1.rbegin();
return 1ll*cnt*x-sum1+sum2-1ll*(st1.size()+st2.size()-cnt)*x;
}
} x1,x2;
int n,k,m;
struct data{
int a,b;
friend bool operator <(data x,data y){
return x.a+x.b<y.a+y.b;
}
} f[MAXN];
ll sum=0;
ll pre[MAXN],suf[MAXN];
int main(){
scanf("%d%d",&k,&n);
for(int i=1;i<=n;i++){
char x,y;int a,b;cin>>x>>a>>y>>b;
if(x==y) sum+=abs(a-b);
else f[++m].a=a,f[m].b=b,sum++;
}
if(k==1){
for(int i=1;i<=m;i++) x1.insert(f[i].a),x1.insert(f[i].b);
printf("%lld\n",x1.query()+sum);
} else {
sort(f+1,f+m+1);
for(int i=1;i<=m;i++) x1.insert(f[i].a),x1.insert(f[i].b),pre[i]=x1.query();
for(int i=m;i;i--) x2.insert(f[i].a),x2.insert(f[i].b),suf[i]=x2.query();
ll mn=1e18;
for(int i=1;i<=m+1;i++) mn=min(mn,pre[i-1]+suf[i]);
printf("%lld\n",mn+sum);
}
return 0;
}

洛谷 P3644 [APIO2015]八邻旁之桥(对顶堆维护中位数)的更多相关文章

  1. 洛谷 P3644 [APIO2015]八邻旁之桥 解题报告

    P3644 [APIO2015]八邻旁之桥 题目描述 一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域\(A\)和区域\(B\). 每一块区域沿着河岸都建了恰好\(1000000001\)栋的建筑 ...

  2. [APIO2015]八邻旁之桥——非旋转treap

    题目链接: [APIO2015]八邻旁之桥 对于$k=1$的情况: 对于起点和终点在同侧的直接计入答案:对于不在同侧的,可以发现答案就是所有点坐标与桥坐标的差之和+起点与终点不在同一侧的人数. 将所有 ...

  3. [BZOJ4071][APIO2015]八邻旁之桥

    BZOJ(这题是BZOJ权限题,有权限号的就去看看吧) Luogu(良心洛谷) 题目描述 一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域\(A\)和区域\(B\). 每一块区域沿着河岸都建了恰好 ...

  4. [APIO2015]八邻旁之桥

    题面在这里 sol 这是一个\(Splay\)的题解 首先,如果一个人的家和办公室在同一侧,我们可以直接预处理; 如果不在同一侧,也可以加上1(当然要过桥啦) 当k==1时 我们设桥的位置为\(pos ...

  5. 题解【luoguP3644 [APIO2015]八邻旁之桥】

    题目链接 题解 家和公司在同侧 简单,直接预处理掉 若 \(k=1\) 取所有的居民的\(\frac{家坐标+公司坐标}{2}\)的所有坐标的正中间建一座桥,使所有居民到的距离最小. 实现方法:线段树 ...

  6. [luoguP3644] [APIO2015]八邻旁之桥(权值线段树)

    传送门 首先如果起点终点都在同一侧可以直接处理,如果需要过桥答案再加1 对于k等于1的情况 桥的坐标为x的话,a和b为起点和终点坐标 $ans=\sum_{1}^{n} abs(a_{i}-x)+ab ...

  7. APIO2015 八邻旁之桥/巴邻旁之桥

    题目描述: bz luogu 题解: 贪心+权值线段树. $K=1$的时候,答案为$\sum |x-l| + |x-r|$,所以所有端点排序后取中位数即可. $K=2$的时候,一定是左边的一些走左边的 ...

  8. 【BZOJ4071】八邻旁之桥(线段树)

    [BZOJ4071]八邻旁之桥(线段树) 题面 BZOJ权限题,洛谷链接 题解 既然\(k<=2\) 那么,突破口就在这里 分类讨论 ①\(k=1\) 这...不就是中位数吗.... 直接把所有 ...

  9. 【BZOJ4071】[Apio2015]巴邻旁之桥 Treap

    [BZOJ4071][Apio2015]巴邻旁之桥 Description 一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 A 和区域 B. 每一块区域沿着河岸都建了恰好 1000000001 ...

随机推荐

  1. 初学Python “登录”案例 更新!!

    更新内容:添加了登录次数,如果超过限制的次数,则提示账户被锁定,去某邮箱申请解锁账户! 此次仅把登录系统更新之后源代码放到这里,不在共享源文件在网盘了! 1 ''' 2 登录界面 3 ''' 4 5 ...

  2. SpringCloud-SpringBoot-SpringCloudAlibaba对应版本选择

    一.SpringCloud-SpringBoot 对应的版本选择 SpringCloud官网常规方式只能查看最新的几个版本信息 https://spring.io/projects/spring-cl ...

  3. java中this关键字总结

    1.this是一个引用,也是一个变量,存储在JVM堆内存的Java对象内部. 2.this变量中保存的内存地址指向自身. 3.this可以在实例方法中使用,this指向当前正在执行这个动作的对象(th ...

  4. BUAA2020软工作业(一)——谈谈我和计算机的缘分

    项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 第一次作业-热身! 我在这个课程的目标是 进一步提高自己的编码能力,工程能力 这个作业在哪个具体方 ...

  5. Noip模拟39 2021.8.14

    T1 打地鼠 都切掉了的简单题 1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 con ...

  6. 21.8.7 test

    \(NOIP\) 测试 考的一般般. \(T1\) WOJ4656 签到题,其实就是算 \(\sum\limits_{i=1}^n i^2\) #include<bits/stdc++.h> ...

  7. CODING —— 云原生时代的研发工具领跑者

    本文为 CODING 创始人兼 CEO 张海龙在腾讯云 CIF 工程效能峰会上所做的分享. 文末可前往峰会官网,观看回放并下载 PPT. 大家上午好,很高兴能有机会与大家分享 CODING 最近的一些 ...

  8. SPOJ GSS8 - Can you answer these queries VIII | 平衡树

    题目链接 这一道题的修改操作用平衡树都很容易实现,难处理的是询问操作. 要想解决询问操作,只要知道如何在平衡树上快速合并左右两个区间的答案即可. 设$Ans_{[l,r]}^k=\sum\limits ...

  9. 【Azure 存储服务】如何把开启NFS 3.0协议的Azure Blob挂载在Linux VM中呢?(NFS: Network File System 网络文件系统)

    问题描述 如何把开启NFS协议的Azure Blob挂载到Linux虚拟机中呢? [答案]:可以使用 NFS 3.0 协议从基于 Linux 的 Azure 虚拟机 (VM) 或在本地运行的 Linu ...

  10. float32 和 float64

    float32 和 float64 Go语言中提供了两种精度的浮点数 float32 和 float64. float32,也即我们常说的单精度,存储占用4个字节,也即4*8=32位,其中1位用来符号 ...