【Luogu】 P3644 [APIO2015] 八邻旁之桥

题目链接

点击打开链接

题目解法

在同一边的情况一定不会过桥,可以直接算出来

这里只考虑不在同一边的情况,一定需要过一次桥

发现家和办公室的位置可以互换,下面假设家在A岸,办公室在B岸(其实没什么用)

  1. 首先考虑 k=1 的情况,如果家在 a 处,办公室在 b 处,桥在 p 处
    那么从家到办公室的距离就是 \left | a-p \right |+\left | b-p \right |+1 ,其中 +1 可以提前计算,这里不考虑可以发现,a 和 b 对 p 的贡献可以分开算,就是它们到 p 的距离题意可以抽象成,有一些点,询问位置 p ,使得 p 到这些点的距离之和最小
    p 显然取中位数最优
  2. 考虑 k=2 的情况,我们希望能将居民分成 2 部分,使得第一部分和第二部分都转化为 k=1 的情况
    我们发现有 2 个桥的情况,走离 \frac{a+b}{2} 的桥更优
    这里画个图,分类讨论一下就比较显然了
    可以发现这里满足排序贪心的性质
    如果有桥 p1, p2(p1<p2),居民 a1, b1, a2, b2(a1+b1<a2+b2)
    如果居民1走桥 p2,那么居民2一定走桥p2
    反之也成立
    我们发现可以枚举断点,断点左边都走左边的桥,断点右边都走右边的桥
    这里需要动态维护中位数,用对顶堆即可
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int> pii;
const int N(100100),inf(1e18);
int k,n,ans,sum[N<<1];
int nums[N<<1];
pii a[N];
int t1[N],t2[N];
int siz1,siz2,sum1,sum2;
priority_queue<int> que1;
priority_queue<int,vector<int>,greater<int> > que2;
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar())
		if(ch=='-')
			RR=-1;
	for(;isdigit(ch);ch=getchar())
		FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
void solve1(){
	for(int i=1,s,t;i<=n;i++){
		char p[2],q[2];
		scanf("%s",p);s=read();scanf("%s",q);t=read();
		if(p[0]!=q[0]) nums[(i<<1)-1]=t,nums[i<<1]=s;
		else ans+=abs(t-s),i--,n--;
	}
	sort(nums+1,nums+(n<<1)+1);
	int pos=nums[n];
	for(int i=1;i<=n<<1;i++)
		ans+=abs(nums[i]-pos);
	printf("%lld",ans+n);
}
bool cmp(const pii &x,const pii &y){
	return x.first+x.second<y.first+y.second;
}
void insert(int x){
	if(que1.empty()||x<=que1.top()) que1.push(x),siz1++,sum1+=x;
	else que2.push(x),siz2++,sum2+=x;
}
void balance(){
	while(siz1<siz2){
		int t=que2.top();
		que1.push(t),siz1++,sum1+=t;
		que2.pop(),siz2--,sum2-=t;
	}
	while(siz1-1>=siz2+1){
		int t=que1.top();
		que2.push(t),siz2++,sum2+=t;
		que1.pop(),siz1--,sum1-=t;
	}
}
void solve2(){
	for(int i=1,s,t;i<=n;i++){
		char p[2],q[2];
		scanf("%s",p);s=read();scanf("%s",q);t=read();
		if(p[0]!=q[0]) ans++,a[i]=make_pair(s,t);
		else ans+=abs(t-s),i--,n--;
	}
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++){
		insert(a[i].first),insert(a[i].second);
		balance();
		t1[i]=que1.top()*siz1-sum1+sum2-que1.top()*siz2;
	}
	while(!que1.empty()) que1.pop();
	while(!que2.empty()) que2.pop();
	siz1=siz2=sum1=sum2=0;
	for(int i=n;i>=1;i--){
		insert(a[i].first),insert(a[i].second);
		balance();
		t2[i]=que1.top()*siz1-sum1+sum2-que1.top()*siz2;
	}
	int ans2=inf;
	for(int i=0;i<=n;i++)
		ans2=min(ans2,t1[i]+t2[i+1]);
	printf("%lld",ans+ans2);
}
signed main(){
	k=read(),n=read();
	if(k==1) solve1();
	else solve2();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值