Problem
n
n
个人,住在 或
B
B
岸,在 或
B
B
岸上班,距离为坐标相减(如需过河则加一)。
可以修建 座桥,问所有人从家到公司所需最短时间和是多少
Solution
发现 k k 只有两种取值,我们分别讨论。
- 时
当我们选择 pos p o s 为桥时,所得的结果是 ans=∑i=1nabs(xi−pos)+abs(yi−pos) a n s = ∑ i = 1 n a b s ( x i − p o s ) + a b s ( y i − p o s )
显然,将桥选在中间为最优。
因此将所有数(两岸)一起排序,中位数就是桥的位置。- k=2 k = 2 时
上面我们发现中位数为最优,那么两座桥的时候,人会怎么选择呢?
显然人会走向距离 xi+yi2 x i + y i 2 最近的
因此我们可以把所有人按 xi+yi2 x i + y i 2 排序,前一部分人走左桥,有一部分人走右桥。
那么问题就转化为区间求中位数。
显然可以平衡树实现(常数大+代码长…)
而权值线段树也可以解决(因为只会求前一段、后一段,中位数查个数就可以了)对于每一段的结果,如前一段为例,
ans=pos∗cnt1−sum1+sum2−pos∗cnt2=pos∗cnt1−sum1+(sum−sum1)−pos∗(cnt−cnt1)=pos∗(2∗cnt−cnt1)+sum−2∗sum1 a n s = p o s ∗ c n t 1 − s u m 1 + s u m 2 − p o s ∗ c n t 2 = p o s ∗ c n t 1 − s u m 1 + ( s u m − s u m 1 ) − p o s ∗ ( c n t − c n t 1 ) = p o s ∗ ( 2 ∗ c n t − c n t 1 ) + s u m − 2 ∗ s u m 1
(其中 pos p o s 为中位数, sum1 s u m 1 为小于中位数的数的和, sum2 s u m 2 为小于中位数的数的和, sum s u m 为这一部分所有数的和, cnt1 c n t 1 为小于中位数的数的个数, cnt2 c n t 2 为小于中位数的数的个数, cnt c n t 为这一部分所有数的个数)注意:
- 过河也需要时间…
- 权值线段树,因此要离散化
- 树中有 2n 2 n 个数,中位数也就是第 n n 小或第 大
- 注意空间…离散化以后会有至多 2m 2 m 个点,因此线段树要开 8 8 <script type="math/tex" id="MathJax-Element-101">8</script> 倍
- 除了我可能没有人会这样wa…要先按和排序再离散化….先离散化以后和就边了啊啊啊….
Code
#include <cstdio> #include <cmath> #include <algorithm> using namespace std; #define N 100010 #define ll long long char str1[5],str2[5]; int num=0,cnt=0,k,n; ll b[N<<1]; ll ans=0; struct node{int x,y;}a[N]; inline bool cmp(node x,node y){return x.x+x.y<y.x+y.y;} struct Segment{ ll s,sum[N<<3]; int tot,sz[N<<3]; inline void init(){ s=0;tot=0; } inline void ins(int v,int l,int r,int x,int val){ sz[v]+=val;sum[v]+=b[x]*val; if(l==r) return; int mid=l+r>>1; if(x<=mid) ins(v<<1,l,mid,x,val); else ins(v<<1|1,mid+1,r,x,val); } inline int query(int v,int l,int r,int x){ if(l==r){ s+=sum[v];tot+=sz[v]; return l; }int mid=l+r>>1; if(x<=sz[v<<1]) return query(v<<1,l,mid,x); s+=sum[v<<1];tot+=sz[v<<1];return query(v<<1|1,mid+1,r,x-sz[v<<1]); } }t[2]; int main(){ scanf("%d%d",&k,&n); for(int i=1;i<=n;i++){ int x,y;scanf("%s%d%s%d",str1,&x,str2,&y); if(str1[0]==str2[0]){ if(x>y) swap(x,y); ans=ans+(ll)y-(ll)x; }else a[++num].x=x,a[num].y=y,b[++cnt]=x,b[++cnt]=y; }ans+=num; if(!num){printf("%lld\n",ans);return 0;} if(k==1){ sort(b+1,b+cnt+1);ll x=((ll)b[cnt/2]+(ll)b[cnt/2+1])/2; for(int i=1;i<=cnt;i++){ if(b[i]>x) ans=ans+b[i]-x; else ans=ans-b[i]+x; } printf("%lld\n",ans); }else{ sort(b+1,b+cnt+1);cnt=unique(b+1,b+cnt+1)-b-1; sort(a+1,a+num+1,cmp); for(int i=1;i<=num;i++){ a[i].x=lower_bound(b+1,b+cnt+1,a[i].x)-b; a[i].y=lower_bound(b+1,b+cnt+1,a[i].y)-b; t[1].ins(1,1,cnt,a[i].x,1);t[1].ins(1,1,cnt,a[i].y,1); } t[1].init();ll pos=b[t[1].query(1,1,cnt,num)]; ll mn=pos*(2ll*t[1].tot-(ll)t[1].sz[1])+t[1].sum[1]-2*t[1].s; for(int i=1;i<=num;i++){ t[1].ins(1,1,cnt,a[i].x,-1);t[1].ins(1,1,cnt,a[i].y,-1); t[0].ins(1,1,cnt,a[i].x,1);t[0].ins(1,1,cnt,a[i].y,1); t[0].init();t[1].init(); pos=b[t[0].query(1,1,cnt,i)]; ll res=pos*(2ll*t[0].tot-(ll)t[0].sz[1])+t[0].sum[1]-2*t[0].s; pos=b[t[1].query(1,1,cnt,num-i)]; res+=pos*(2ll*t[1].tot-(ll)t[1].sz[1])+t[1].sum[1]-2*t[1].s; mn=min(mn,res); } printf("%lld\n",ans+mn); } return 0; }