bzoj4071【APIO2015】巴邻旁之桥

148 篇文章 0 订阅
11 篇文章 0 订阅

4071: [Apio2015]巴邻旁之桥 

Time Limit: 20 Sec   Memory Limit: 256 MB
Submit: 99   Solved: 45
[ Submit][ Status][ Discuss]

Description

 一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 A 和区域 B。

每一块区域沿着河岸都建了恰好 1000000001 栋的建筑,每条岸边的建筑都从 0 编号到 1000000000。相邻的每对建筑相隔 1 个单位距离,河的宽度也是 1 个单位长度。区域 A 中的 i 号建筑物恰好与区域 B 中的 i 号建筑物隔河相对。
城市中有 N 个居民。第 i 个居民的房子在区域 Pi 的 Si 号建筑上,同时他的办公室坐落在 Qi 区域的 Ti 号建筑上。一个居民的房子和办公室可能分布在河的两岸,这样他就必须要搭乘船只才能从家中去往办公室,这种情况让很多人都觉得不方便。为了使居民们可以开车去工作,政府决定建造不超过 K 座横跨河流的大桥。
由于技术上的原因,每一座桥必须刚好连接河的两岸,桥梁必须严格垂直于河流,并且桥与桥之间不能相交。当政府建造最多 K 座桥之后,设 Di 表示第 i 个居民此时开车从家里到办公室的最短距离。请帮助政府建造桥梁,使得 D1+D2+⋯+DN 最小。

Input

输入的第一行包含两个正整数 K 和 N,分别表示桥的上限数量和居民的数量。

接下来 N 行,每一行包含四个参数:Pi,Si,Qi 和 Ti,表示第 i 个居民的房子在区域 Pi 的 Si 号建筑上,且他的办公室位于 Qi 区域的 Ti 号建筑上。

Output

输出仅为一行,包含一个整数,表示 D1+D2+⋯+DN 的最小值。

Sample Input

1 5
B 0 A 4
B 1 B 3
A 5 B 7
B 2 A 6
B 1 A 7

Sample Output

24

HINT

 子任务


所有数据都保证:Pi 和 Qi 为字符 “A” 和 “B” 中的一个, 0≤Si,Ti≤1000000000,同一栋建筑内可能有超过 1 间房子或办公室(或二者的组合,即房子或办公室的数量同时大于等于 1)。


子任务 1 (8 分)

K=1

1≤N≤1000

子任务 2 (14 分)

K=1

1≤N≤100000

子任务 3 (9 分)

K=2

1≤N≤100

子任务 4 (32 分)

K=2

1≤N≤1000

子任务 5 (37 分)

K=2

1≤N≤100000

Source




首先如果办公室和家在同一侧,直接将距离加到答案中即可。

那么办公室和家不在同一侧应该如何解决呢?对于k=1,显然只需要将桥建在所有位置的中位数即可。

对于k=2,可以发现每个人都会选择距离家和办公室中点较近的桥行走。那么我们就可以按照家和办公室中点将每个人排序,枚举分割点,将分割点前后的人按k=1的情况分别处理。问题就转化动态维护区间中位数了,可以用平衡树处理。(方法类似bzoj1112)

这道题将所有人按中点排序的思路很好。




#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define LL long long
#define MAXN 200100
using namespace std;
int n,root,tot,cnt=0;
LL m,x,y,ans=0,mn,a[MAXN],f1[MAXN],f2[MAXN];
char cx,cy;
struct tree_type
{
	int l,r;
	LL s,rnd,sum,v,w;
}t[MAXN];
struct node
{
	LL x,y;
}b[MAXN];
inline LL read()
{
	LL ret=0,flag=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') flag=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
	return ret*flag;
}
inline char readch()
{
	char ch=getchar();
	while (ch!='A'&&ch!='B') ch=getchar();
	return ch;
}
inline void solve1()
{
	F(i,1,n)
	{
		cx=readch();x=read();cy=readch();y=read();
		if (cx==cy) ans+=abs(x-y);
		else a[++cnt]=x,a[++cnt]=y,ans++;
	}
	sort(a+1,a+cnt+1);
	F(i,1,cnt) ans+=abs(a[i]-a[cnt>>1]);
	printf("%lld\n",ans);
}
inline bool cmp(node n1,node n2){return n1.x+n1.y<n2.x+n2.y;}
inline void pushup(int k){t[k].s=t[t[k].l].s+t[t[k].r].s+t[k].w;t[k].sum=t[t[k].l].sum+t[t[k].r].sum+t[k].w*t[k].v;}
inline void rturn(int &k){LL tmp=t[k].l;t[k].l=t[tmp].r;t[tmp].r=k;t[tmp].s=t[k].s;t[tmp].sum=t[k].sum;pushup(k);k=tmp;}
inline void lturn(int &k){LL tmp=t[k].r;t[k].r=t[tmp].l;t[tmp].l=k;t[tmp].s=t[k].s;t[tmp].sum=t[k].sum;pushup(k);k=tmp;}
inline void ins(int &k,LL x)
{
	if (!k){k=++tot;t[k].s=t[k].w=1;t[k].sum=t[k].v=x;t[k].l=t[k].r=0;t[k].rnd=rand();return;}
	t[k].s++;t[k].sum+=x;
	if (t[k].v==x) t[k].w++;
	else if (t[k].v>x){ins(t[k].l,x);if (t[t[k].l].rnd<t[k].rnd) rturn(k);}
	else{ins(t[k].r,x);if (t[t[k].r].rnd<t[k].rnd) lturn(k);}
}
inline void del(int &k,LL x)
{
	if (t[k].v==x)
	{
		if (t[k].w>1){t[k].s--;t[k].w--;t[k].sum-=x;}
		else if (!t[k].l||!t[k].r) k=t[k].l+t[k].r;
		else if (t[t[k].l].rnd<t[t[k].r].rnd){rturn(k);del(k,x);}
		else{lturn(k);del(k,x);}
		return;
	}
	t[k].s--;t[k].sum-=x;
	if (x<t[k].v) del(t[k].l,x);
	else del(t[k].r,x);
}
inline LL getans(int k,int x)
{
	LL ln=t[t[k].l].s;
	if (ln<x&&ln+t[k].w>=x){m+=t[t[k].l].sum+(x-ln)*t[k].v;return t[k].v;}
	else if (ln>=x) return getans(t[k].l,x);
	else{m+=t[t[k].l].sum+t[k].w*t[k].v;return getans(t[k].r,x-ln-t[k].w);}
}
inline LL calc(int q)
{
	m=0;
	LL tmp=getans(root,q);
	LL ret=t[root].sum-m*2;
	return ret;
}
inline void solve2()
{
	F(i,1,n)
	{
		cx=readch();x=read();cy=readch();y=read();
		if (cx==cy) ans+=abs(x-y);
		else b[++cnt].x=x,b[cnt].y=y,ans++;
	}
	sort(b+1,b+cnt+1,cmp);
	f1[0]=root=tot=0;
	F(i,1,cnt)
	{
		ins(root,b[i].x);ins(root,b[i].y);
		f1[i]=calc(i);
	}
	f2[cnt+1]=root=tot=0;
	D(i,cnt,1)
	{
		ins(root,b[i].x);ins(root,b[i].y);
		f2[i]=calc(cnt-i+1);
	}
	mn=f1[0]+f2[1];
	F(i,1,cnt) mn=min(f1[i]+f2[i+1],mn);
	ans+=mn;
	printf("%lld\n",ans);
}
int main()
{
	LL ff=read();
	n=read();
	if (ff==1) solve1();
	else solve2();
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值