BZOJ4071 & 洛谷3644:[APIO2015]巴邻旁之桥——题解
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ4071 & 洛谷3644:[APIO2015]巴邻旁之桥——题解相关的知识,希望对你有一定的参考价值。
https://www.lydsy.com/JudgeOnline/problem.php?id=4071
https://www.luogu.org/problemnew/show/P3644
一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 A 和区域 B。
每一块区域沿着河岸都建了恰好 1000000001 栋的建筑,每条岸边的建筑都从 0 编号到 1000000000。相邻的每对建筑相隔 1 个单位距离,河的宽度也是 1 个单位长度。区域 A 中的 i 号建筑物恰好与区域 B 中的 i 号建筑物隔河相对。城市中有 N 个居民。第 i 个居民的房子在区域 Pi 的 Si 号建筑上,同时他的办公室坐落在 Qi 区域的 Ti 号建筑上。一个居民的房子和办公室可能分布在河的两岸,这样他就必须要搭乘船只才能从家中去往办公室,这种情况让很多人都觉得不方便。为了使居民们可以开车去工作,政府决定建造不超过 K 座横跨河流的大桥。由于技术上的原因,每一座桥必须刚好连接河的两岸,桥梁必须严格垂直于河流,并且桥与桥之间不能相交。当政府建造最多 K 座桥之后,设 Di 表示第 i 个居民此时开车从家里到办公室的最短距离。请帮助政府建造桥梁,使得 D1+D2+⋯+DN 最小。
参考:https://www.cnblogs.com/zhenghaotian/p/8304917.html
对于同岸和走桥的代价先预处理,下面不在阐述。
k=1时,相当于找到一个点,使得所有起点和终点到该点的距离和最小。则我们排序在中间两点中间建桥则一定最优。就不证明了。
k=2时不能按k=1做(因为起点和终点需要用的桥是一样的),但是选桥的代价为(A起点,B桥,C终点)A->B->C,显然选一个近的桥最优,用程序表达的话就是离AC中点最近的桥。
所以以此排序,枚举分界点,其左右都是k=1的情况,用线段树做即可。
(简单聊下心路历程:开始k=1秒后想k=2,没考虑起点终点桥一样以为三分可过,结果第二个样例就跪了,后来思考之后排序后三分是O(nlog^2n)结果洛谷评测机死活没卡过去TAT果然还是太菜了我)
#include<cstdio> #include<queue> #include<cctype> #include<cstring> #include<cmath> #include<vector> #include<algorithm> using namespace std; typedef long long ll; const int N=200100; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch==\'-\';ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } inline char getc(){ char ch=0; while(ch!=\'A\'&&ch!=\'B\')ch=getchar(); return ch; } struct node{ int l,r; }q[N]; int m,tot,b[N]; ll num[2][N*4],sum[2][N*4],L[2],R[2]; bool cmp(node a,node b){return a.l+a.r<b.l+b.r;} void LSH(){ sort(b+1,b+m+1); m=unique(b+1,b+m+1)-b-1; for(int i=1;i<=tot;i++){ q[i].l=lower_bound(b+1,b+m+1,q[i].l)-b; q[i].r=lower_bound(b+1,b+m+1,q[i].r)-b; } } void insert(int a,int l,int r,int k,ll w,int p){ num[p][a]+=w,sum[p][a]+=w*b[k]; if(l==r)return; int mid=(l+r)>>1; if(k<=mid)insert(a*2,l,mid,k,w,p); else insert(a*2+1,mid+1,r,k,w,p); } int find(int a,int l,int r,int k,int p){ if(l==r){ L[0]+=sum[p][a],L[1]+=num[p][a]; return b[l]; } int mid=(l+r)>>1; if(num[p][a*2]>=k){ R[0]+=sum[p][a*2+1],R[1]+=num[p][a*2+1]; return find(a*2,l,mid,k,p); }else{ L[0]+=sum[p][a*2],L[1]+=num[p][a*2]; return find(a*2+1,mid+1,r,k-num[p][a*2],p); } } int main(){ int k=read(),n=read(); ll ans=0; for(int i=1;i<=n;i++){ char ch1=getc(); ll u=read(); char ch2=getc(); ll v=read(); if(ch1==ch2){ ans+=abs(u-v); continue; } ans++; b[++m]=u,b[++m]=v; if(k==2){ q[++tot]=(node){u,v}; } } if(k==1){ sort(b+1,b+m+1); for(int i=1,j=m;i<j;i++,j--)ans+=b[j]-b[i]; printf("%lld\\n",ans); }else{ if(!tot){ printf("%lld\\n",ans); return 0; } sort(q+1,q+tot+1,cmp); LSH(); for(int i=1;i<=tot;i++){ insert(1,1,m,q[i].l,1,1); insert(1,1,m,q[i].r,1,1); } int x=find(1,1,m,tot,1); ll tmp=x*L[1]-L[0]+R[0]-x*R[1]; for(int i=1;i<tot;i++){ insert(1,1,m,q[i].l,1,0); insert(1,1,m,q[i].r,1,0); insert(1,1,m,q[i].l,-1,1); insert(1,1,m,q[i].r,-1,1); ll all=0; L[0]=L[1]=R[0]=R[1]=0; x=find(1,1,m,i,0); all+=x*L[1]-L[0]+R[0]-x*R[1]; L[0]=L[1]=R[0]=R[1]=0; x=find(1,1,m,tot-i,1); all+=x*L[1]-L[0]+R[0]-x*R[1]; tmp=min(tmp,all); } printf("%lld\\n",ans+tmp); } return 0; }
+++++++++++++++++++++++++++++++++++++++++++
+本文作者:luyouqi233。 +
+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+
+++++++++++++++++++++++++++++++++++++++++++
以上是关于BZOJ4071 & 洛谷3644:[APIO2015]巴邻旁之桥——题解的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ3339:Rmq Problem & BZOJ3585 & 洛谷4137:mex——题解
BZOJ1229 & 洛谷2917:[USACO2008 NOV]toy 玩具 & 洛谷4480:[BJWC2018]餐巾计划问题——题解
BZOJ3675 & 洛谷3648 & UOJ104:[Apio2014]序列分割——题解