题面在这里

sol

这是一个\(Splay\)的题解

首先,如果一个人的家和办公室在同一侧,我们可以直接预处理;

如果不在同一侧,也可以加上1(当然要过桥啦)

当k==1时

我们设桥的位置为\(pos\),每个人的家的位置为\(x[i]\),办公室的位置为\(y[i]\),

则总代价为\(\sum_{i=1}^n (abs(x_i-pos)+abs(y_i-pos))\)

从这里我们可以看到,其实家和办公室的区别不是很明显。

所以这个问题可以简化为:

在数轴上任取一点a,最小化 \(\sum abs(a-x_i)\)

那么我们将所有家和办公室按照坐标排序,桥的位置肯定就在中间两个端点的位置之间

至于怎么统计相信大家都会吧(就在下面)

把所有家和办公室的坐标丢进一棵Splay中,平分

统计出左边的sum和右边的sum,左边的sz和右边的sz

当k==2时

再将上面的式子细分一下,我们能发现:

对于每条路径\(x[i]->y[i]\),其实际长度和\(1/2(x[i]+y[i])\)距离桥的距离有关

于是我们可以考虑将所有路径按照\((x[i]+y[i])\)排序

考虑建立两颗Splay

首先把所有的节点全部插入一棵Splay中去

然后一对一对(注意此处)的从老Splay中丢到另一棵Splay中去,一边统计答案

复杂度是O(nlogn)

应该是做完了

什么!!!!你被卡常了!!!!

在下提交的时候,第二个点总是TLE...交了无数遍的95分

看着机房的其他dalao都是用线段树做的,并不是很甘心啊......

但是方法总比困难多(hhhh)

具体可以参考我的代码

(一遍过的dalao可以略过)

代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define RG register
#define isr(i) (s[1][fa[(i)]]==(i)) using namespace std;
typedef long long ll;
const int N=200010;
const int inf=2147483647;
int cntt;
ll Ans[N]; struct line{
int l,r;
bool operator <(const line &a)const{
return (l+r)<(a.l+a.r);
}
}t[N]; inline int read()
{
RG int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
} bool cmp(line a,line b){return (a.l+a.r)<(b.l+b.r);} struct Splay{
int root,tot,j,k;
int s[2][N],fa[N],sz[N],cnt[N];
ll sum[N],v[N]; inline bool empty(){return !(bool)sz[root];} inline void clear(){
root=tot=0;
memset(s,0,sizeof(s));
memset(fa,0,sizeof(fa));
memset(sz,0,sizeof(sz));
memset(sum,0,sizeof(sum));
memset(cnt,0,sizeof(cnt));
memset(v,0,sizeof(v));
} inline void init(int i,int x,int ff){
s[0][i]=s[1][i]=0;fa[i]=ff;
v[i]=sum[i]=x;cnt[i]=sz[i]=1;
} inline void update(int i){
sz[i]=sz[s[0][i]]+sz[s[1][i]]+cnt[i];
sum[i]=sum[s[0][i]]+sum[s[1][i]]+cnt[i]*v[i];
} inline void rot(int i){
j=fa[i];k=fa[j];
RG bool b=isr(i);
fa[i]=k;s[isr(j)][k]=i;
if(s[!b][i])fa[s[!b][i]]=j;s[b][j]=s[!b][i];
fa[j]=i;s[!b][i]=j;
update(j);
} inline void splay(int i,int a){
if(!a)root=i;
while(fa[i]^a){
j=fa[i];
if(fa[j]^a)
isr(i)^isr(j)?rot(i):rot(j);
rot(i);
}
update(i);
} inline void insert(int x){
RG int i=root,ff=0;
while(v[i]!=x&&i){
ff=i;i=s[v[i]<x][i];
}
if(i&&v[i]==x)cnt[i]++;
else{
i=++tot;
if(ff)s[v[ff]<x][ff]=i;
init(i,x,ff);
if(x==inf||x==-inf){
sz[i]=sum[i]=cnt[i]=0;
}
}
splay(i,0);
} inline int find(int x){
RG int i=root;
while(v[i]!=x&&s[v[i]<x][i])
i=s[v[i]<x][i];
return i;
} inline void Next(int x,int &lst,int &nxt){
RG int i=find(x);splay(i,0);
if(v[i]>x)nxt=i;
else {
nxt=s[1][i];
while(s[0][nxt])nxt=s[0][nxt];
}
if(v[i]<x)lst=i;
else{
lst=s[0][i];
while(s[1][lst])lst=s[1][lst];
}
} inline void Delete(int x){
RG int i=find(x);
cnt[i]--;splay(i,0);
} inline int kth(int k){
RG int i=root;
while(1){
if(sz[s[0][i]]>=k)i=s[0][i];
else if(sz[s[0][i]]+cnt[i]>=k)return i;
else k-=sz[s[0][i]]+cnt[i],i=s[1][i];
}
} inline ll bridge(){//这里是统计答案(不开long long可是会炸飞的)
if(empty())return 0;
int i=kth(sz[root]/2);splay(i,0);
return 1ll*sz[s[0][i]]*v[i]-sum[s[0][i]]+sum[s[1][i]]-1ll*sz[s[1][i]]*v[i];
}
}A,B; inline ll input(ll n){
RG char p[5],q[5];
RG ll ans=0;RG int s,T; cntt=0;
A.insert(inf);A.insert(-inf);
B.insert(inf);B.insert(-inf); for(RG int i=1;i<=n;i++){
scanf("%s",p+1);s=read();
scanf("%s",q+1);T=read();
if(p[1]!=q[1]){
ans++;
t[++cntt].l=s;t[cntt].r=T;
}
else ans+=abs(s-T);
}
sort(t+1,t+cntt+1);
for(RG int i=1;i<=cntt;i++){
A.insert(t[i].l);A.insert(t[i].r);
Ans[i]=A.bridge();
/*
最关键的地方就是这里
直接记录一个Ans数组表示前i条路径全部走到一座桥上的答案
之后就不用删除了,把路径倒着插入另外一棵线段树中统计即可
*/
} return ans;
} inline void work2(int k,int n){ RG ll ans=input(n);
RG ll minn=Ans[cntt];
if(A.empty()){
printf("%lld\n",ans);
return;
} for(RG int i=cntt;i>=1;i--){
B.insert(t[i].l);B.insert(t[i].r);
minn=min(minn,Ans[i-1]+B.bridge());
//倒插统计部分
} printf("%lld\n",minn+ans);
} inline void work1(int k,int n){
printf("%lld\n",input(n)+A.bridge());
} int main()
{
RG int k,n;
k=read();n=read();
if(k^1)work2(k,n);
else work1(k,n);
return 0;
}

[APIO2015]八邻旁之桥的更多相关文章

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

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

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

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

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

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

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

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

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

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

  6. 洛谷 P3644 [APIO2015]八邻旁之桥(对顶堆维护中位数)

    题面传送门 题意: 一条河将大地分为 \(A,B\) 两个部分.两部分均可视为一根数轴. 有 \(n\) 名工人,第 \(i\) 名的家在 \(x_i\) 区域的 \(a_i\) 位置,公司在 \(y ...

  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. 深入理解vue

    一 理解vue的核心理念 使用vue会让人感到身心愉悦,它同时具备angular和react的优点,轻量级,api简单,文档齐全,简单强大,麻雀虽小五脏俱全. 倘若用一句话来概括vue,那么我首先想到 ...

  2. yii2 源码分析 Component类分析 (二)

    转载请注明链接http://www.cnblogs.com/liuwanqiu/p/6739538.html 组件(component),是Yii框架的基类,实现了属性.事件.行为三类功能,它集成自o ...

  3. python学习:99乘法口诀

    #!/usr/bin/python   for i in xrange(1,10):     for j in xrange(1,i+1):         print "%s*%s=%s& ...

  4. navcat无法远程连接mysql数据库解决办法

    navcat无法远程连接mysql数据库,一般都是因为本地ip没有访问权限,服务器上执行下面指令即可解决 mysql -u root -p GRANT ALL PRIVILEGES ON *.* TO ...

  5. 微信开发系列——微信订阅号前端开发利器:WeUI

    前言:年前的两个星期,学习了下微信公众号的开发.后端基本能够基于盛派的第三方sdk能搞定大部分事宜.剩下的就是前端了,关于手机端的浏览器的兼容性,一直是博主的一块心病,因为博主一直专注于bootstr ...

  6. CentOS7上安装Nginx、PHP、MySQL

    一.安装准备 首先由于nginx的一些模块依赖一些lib库,所以在安装nginx之前,必须先安装这些lib库,这些依赖库主要有g++.gcc.openssl-devel.pcre-devel和zlib ...

  7. 安装apache报没有找到VCRUNTIME40.dll错误

    解决办法 在Windows下运行最新版的Apache和php7都需要Visual C++Redistributable 2015,而之前的版本不需要那么高的,这个组件是运行Visual Studio ...

  8. SQL 分组统计 行转列 CASE WHEN 的使用

    原文地址:http://blog.itpub.net/26451903/viewspace-733526 原文在分组统计部分  sql是有问题的     本文已将sql改正   已用红色标记  Cas ...

  9. JVM笔记6-垃圾回收概述

    JVM进行垃圾回收时要考虑哪的问题如下: 1.如何判定对象为垃圾对象? 1.引用计数法:在对象中添加一个引用计数器,当有地方引用这个对象的时候,引用计数器的值就+1,引用失效的时候,计数器的值就-1, ...

  10. 我的Java设计模式-建造者模式

    在未上大学之前,一直有个梦想"I have a dream!",就是能成为一位汽车工程师,一直幻想着开着自己设计的汽车飞奔在公路上,迷倒了万千少女.咳咳~~虽然现在没实现我的dre ...