[APIO2015]巴邻旁之桥

Description:

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

输入格式:
输入的第一行包含两个正整数 \(K\)\(N\),分别表示桥的上限数量和居民的数量。
接下来 \(N\) 行,每一行包含四个参数:\(P_i, S_i, Q_i\)\(T_i\),表示第 \(i\) 个居民的房子在区域 \(P_i\)\(S_i\) 号建筑上,且他的办公室位于 \(Q_i\) 区域的 \(T_i\) 号建筑上。
输出格式:输出仅为一行,包含一个整数,表示 \(D_1 + D_2 + \cdots + D_N\) 的最小值。

Hint:

\(n \le 10^5\)
\(s_i,T_i \le 10^9\)

Solution:

显然对于在一边的居民可以直接统计
如果在两边,则不管是\(A->B\) 还是\(B->A\) 都是一样的

考虑问题的本质
把每个位置抽象成点
当k=1时,桥建在pos处
\(Ans=min \sum |i-pos|\)

这不就取个中位数吗

当k=2时
考虑一左一右划分成两个子问题
但如何将这些居民划分到两座桥上呢?
对于一个居民
发现选择离 $\frac{s_i + t_i} {2} $ 更近的桥会更优
于是按 $\frac{s_i + t_i} {2} $ 排序
按顺序枚举左和右的分界,每次分别对求中位数,更新答案
由于要进行动态插入和删除,故使用权值线段树实现
需要离散化,代码还是很好懂的

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#define ls p<<1
#define rs p<<1|1
using namespace std;
typedef long long ll;
const ll mxn=1e6+5;
ll n,k,tot,cnt,p[mxn],sz[mxn<<2][2];
ll ans,sum[mxn<<2][2];

struct T {
	ll x,y;
}q[mxn];

ll cmp(T a,T b) {
	return a.x+a.y<b.x+b.y;
}

inline void chkmax(ll &x,ll y) {if(x<y) x=y;}
inline void chkmin(ll &x,ll y) {if(x>y) x=y;}

void solve1()
{
	char a[2],b[2]; ll x,y;
	for(ll i=1;i<=n;++i) {
		scanf("%s %lld %s %lld",a,&x,b,&y);
		if(a[0]==b[0]) ans+=abs(x-y);
		else ++ans,p[++tot]=x,p[++tot]=y;
	}
	sort(p+1,p+tot+1); ll tp=p[tot>>1];
	for(ll i=1;i<(tot>>1);++i) ans+=tp-p[i];
	for(ll i=(tot>>1)+1;i<=tot;++i) ans+=p[i]-tp;
	printf("%lld",ans);
}

void update(ll id,ll l,ll r,ll tp,ll pos,ll val,ll p)
{
	sz[p][id]+=val;
	sum[p][id]+=val*tp;
	if(l==r) return ; ll mid=(l+r)>>1;;
	if(pos<=mid) update(id,l,mid,tp,pos,val,ls);
	else update(id,mid+1,r,tp,pos,val,rs);
}

ll find(ll id,ll l,ll r,ll rk,ll p)
{
	if(l==r) return l; ll mid=(l+r)>>1;
	if(rk<=sz[ls][id]) return find(id,l,mid,rk,ls);
	else return find(id,mid+1,r,rk-sz[ls][id],rs);
}

ll size(ll id,ll l,ll r,ll ql,ll qr,ll p)
{
	if(ql<=l&&r<=qr) return sz[p][id];
	ll mid=(l+r)>>1,res=0;
	if(ql<=mid) res+=size(id,l,mid,ql,qr,ls);
	if(qr>mid) res+=size(id,mid+1,r,ql,qr,rs);
	return res;
} 

ll query(ll id,ll l,ll r,ll ql,ll qr,ll p) 
{
	if(ql<=l&&r<=qr) return sum[p][id];
	ll mid=(l+r)>>1; ll res=0;
	if(ql<=mid) res+=query(id,l,mid,ql,qr,ls);
	if(qr>mid) res+=query(id,mid+1,r,ql,qr,rs);
	return res;
}

void solve2()
{
	char a[2],b[2]; ll x,y;
	for(ll i=1;i<=n;++i) {
		scanf("%s %lld %s %lld",a,&x,b,&y);
		if(a[0]==b[0]) ans+=abs(x-y);
		else ++ans,p[++tot]=x,p[++tot]=y,q[++cnt]=(T){x,y};
	}
	if(!cnt) {printf("%lld",ans); exit(0);} //特判
	sort(p+1,p+tot+1);
	tot=unique(p+1,p+tot+1)-p-1;
	sort(q+1,q+cnt+1,cmp);
	for(ll i=1;i<=cnt;++i) {
		q[i].x=lower_bound(p+1,p+tot+1,q[i].x)-p;
		q[i].y=lower_bound(p+1,p+tot+1,q[i].y)-p;
	}
	for(ll i=1;i<=cnt;++i) 
		update(1,1,tot,p[q[i].x],q[i].x,1,1),update(1,1,tot,p[q[i].y],q[i].y,1,1);	
	ll res=1e18;
	for(ll i=1;i<=cnt;++i) {
		update(0,1,tot,p[q[i].x],q[i].x,1,1),update(0,1,tot,p[q[i].y],q[i].y,1,1);
		update(1,1,tot,p[q[i].x],q[i].x,-1,1),update(1,1,tot,p[q[i].y],q[i].y,-1,1);
		ll lmid=find(0,1,tot,i,1),rmid=find(1,1,tot,cnt-i,1);
		ll u=1ll*size(0,1,tot,1,lmid,1)*p[lmid]-query(0,1,tot,1,lmid,1)+
			 query(0,1,tot,lmid,tot,1)-size(0,1,tot,lmid,tot,1)*p[lmid];
		ll v=1ll*size(1,1,tot,1,rmid,1)*p[rmid]-query(1,1,tot,1,rmid,1)+
             query(1,1,tot,rmid,tot,1)-size(1,1,tot,rmid,tot,1)*p[rmid];
		res=min(res,u+v); 
	}
	printf("%lld",ans+res);
}

int main()
{
	scanf("%lld%lld",&k,&n);
	if(k==1) solve1();
	else solve2();
    return 0;
}

posted @ 2019-03-02 10:30  cloud_9  阅读(170)  评论(0编辑  收藏  举报