bzoj 4071
数据范围很重要!!!
首先观察数据范围,发现我们要修的桥的数量只能是一座或两座,因此我们直接讨论即可
对于在河的一侧的人家,显然花费是一定的,直接累计即可
对于在河两侧的人家,显然过河的花费是一定的,直接累计即可
接下来讨论的所有人都在桥的两侧
首先我们假设只有一座桥,位置为$x$,设对于第$i$个人,他家的位置是$a_{i}$,办公室的位置是$b_{i}$,那么代价就是$|x-a_{i}|+|x-b_{i}|$
因此总代价即为$\sum_{i=1}^{n}|x-a_{i}|+|x-b_{i}|$
那么这是一个绝对值函数,很显然在所有$a_{i}$,$b_{i}$的中位数处取得最小值,因此只需排序后取出中位数计算即可
接下来考虑两座桥的情况
我们发现,修建两座桥之后,一个点将选择代价最小的一座桥通过(废话)
设两座桥位置为$x_{1}$,$x_{2}$,而一个家的位置为$a$,办公室位置为$b$(设$a<b$),那么代价即为$min(|a-x_{1}|+|b-x_{1}|,|a-x_{2}|+|b-x_{2}|)$
那么我们考虑在什么情况下$x_{1}$比$x_{2}$更优
显然,$x_{1}$应当比$x_{2}$离$\frac{a+b}{2}$更近一些!
为什么?
假设$x_{1}$比$x_{2}$离$\frac{a+b}{2}$更近,那么如果两者都在区间$[a,b]$内,那么贡献一样
如果只有一个在区间内,那么一定会是$x_{1}$!
如果都不在区间里,不妨设两者在区间同侧,那么整理一下表达式,也就是$min(a+b-2x_{1},a+b-2x_{2})$
也就是$2min(|\frac{a+b}{2}-x_{1}|,|\frac{a+b}{2}-x_{2}|)$
是不就一目了然了?
因此,如果我们把每个询问按照$a+b$排序,那么我们可以枚举分界点,使得分界点左侧的点都走左侧的桥,分界点右侧的点都走右侧的桥
然后用两棵权值线段树维护中位数和权值和即可
贴代码:
#include <cstdio> #include <cmath> #include <algorithm> #include <map> #define ll long long #define rt1 rt<<1 #define rt2 (rt<<1)|1 using namespace std; const int maxn=200005; ll rnum[maxn]; map <ll,int> M1,M2; int my_stack[200005]; char ch; int n,k; int cnt=0; struct Ques { int lp,rp; friend bool operator < (Ques a,Ques b) { return a.lp+a.rp<b.lp+b.rp; } }q[100005]; int ttop=0; struct Seg_tree { ll siz[maxn<<3],sum[maxn<<3]; void pushup(int rt) { siz[rt]=siz[rt1]+siz[rt2],sum[rt]=sum[rt1]+sum[rt2]; } int get_num(int rt,int l,int r,int rk) { if(l==r)return l; int mid=(l+r)>>1; if(rk<=siz[rt1])return get_num(rt1,l,mid,rk); else return get_num(rt2,mid+1,r,rk-siz[rt1]); } void update(int rt,int l,int r,int pos,int v) { if(l==r){siz[rt]+=v;sum[rt]+=v*rnum[l];return;} int mid=(l+r)>>1; if(pos<=mid)update(rt1,l,mid,pos,v); else update(rt2,mid+1,r,pos,v); pushup(rt); } int query_size(int rt,int l,int r,int lq,int rq) { if(l>=lq&&r<=rq)return siz[rt]; int mid=(l+r)>>1; int s=0; if(lq<=mid)s+=query_size(rt1,l,mid,lq,rq); if(rq>mid)s+=query_size(rt2,mid+1,r,lq,rq); return s; } ll query_sum(int rt,int l,int r,int lq,int rq) { if(l>=lq&&r<=rq)return sum[rt]; int mid=(l+r)>>1; ll s=0; if(lq<=mid)s+=query_sum(rt1,l,mid,lq,rq); if(rq>mid)s+=query_sum(rt2,mid+1,r,lq,rq); return s; } }tr1,tr2; template <typename T>inline void read(T &x) { T c=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();} x=c*f; } void solve() { ll ori=0; for(int i=1;i<=n;i++) { bool fl1=0,fl2=0; ll ql,qr; ch=getchar(); read(ql); fl1=(ch=='A'); ch=getchar(); read(qr); fl2=(ch=='A'); if(fl1==fl2)ori+=abs(qr-ql); else ori++,my_stack[++ttop]=ql,my_stack[++ttop]=qr; } sort(my_stack+1,my_stack+ttop+1); ll temp=my_stack[ttop/2]; for(int i=1;i<=ttop;i++)ori+=abs(temp-my_stack[i]); printf("%lld\n",ori); } int main() { read(k),read(n); if(k==1){solve();return 0;} ll ori=0; for(int i=1;i<=n;i++) { bool fl1=0,fl2=0; ll ql,qr; ch=getchar(); read(ql); fl1=(ch=='A'); ch=getchar(); read(qr); fl2=(ch=='A'); if(fl1==fl2)ori+=abs(qr-ql); else { ori++,q[++ttop]=(Ques){ql,qr}; M1[ql]=M1[qr]=1; } } if(!ttop){printf("%lld\n",ori);return 0;} sort(q+1,q+ttop+1); map <ll,int>::iterator it; for(it=M1.begin();it!=M1.end();it++)M2[it->first]=++cnt,rnum[cnt]=it->first; for(int i=1;i<=ttop;i++) { q[i].lp=M2[q[i].lp],q[i].rp=M2[q[i].rp]; tr2.update(1,1,cnt,q[i].lp,1),tr2.update(1,1,cnt,q[i].rp,1); } ll ans=0x3f3f3f3f3f3f3f3fll; for(int i=1;i<ttop;i++) { tr1.update(1,1,cnt,q[i].lp,1),tr1.update(1,1,cnt,q[i].rp,1); tr2.update(1,1,cnt,q[i].lp,-1),tr2.update(1,1,cnt,q[i].rp,-1); int p1=tr1.get_num(1,1,cnt,i),p2=tr2.get_num(1,1,cnt,ttop-i); ll v0=0,v1=0; v0+=tr1.query_size(1,1,cnt,1,p1)*rnum[p1]-tr1.query_sum(1,1,cnt,1,p1); v0+=tr1.query_sum(1,1,cnt,p1,cnt)-tr1.query_size(1,1,cnt,p1,cnt)*rnum[p1]; v1+=tr2.query_size(1,1,cnt,1,p2)*rnum[p2]-tr2.query_sum(1,1,cnt,1,p2); v1+=tr2.query_sum(1,1,cnt,p2,cnt)-tr2.query_size(1,1,cnt,p2,cnt)*rnum[p2]; ans=min(ans,v0+v1); } printf("%lld\n",ans+ori); return 0; }