洛谷P3644/LOJ2888/UOJ112[APIO2015]巴邻旁之桥 Palembang Bridges(线段树)
首先,家和工作单位在同岸的人不用走桥,以下只讨论家和工作单位在对岸的情况,记第$i$个人家在$a_i$,工作单位在$b_i$(具体哪一岸对答案无影响)
$k=1$时明显是中位数。
$k=2$时,考虑把这些人分为两组,分别取中位数走就可以了。
可以发现每个人走的距离和$\frac{a_i+b_i}{2}$(即中点)有关,所以把人按照$a_i+b_i$排序,对左右两边求中位数,统计答案即可。
现在考虑如何快速求中位数和答案。首先发现中位数是一个特殊的第k大问题,考虑写线段树(因为好写),而答案的话,设数列为$x_1,x_2,\cdots,x_m(x_1\le x_2\le\cdots\le x_m)$,中位数为$k$,由于$\sum\limits^m_{i=1}{|x_i-k|}=\sum\limits^m_{i=1}{(k-x_i)[x_i<k]}+\sum\limits^m_{i=1}{(x_i-k)[x_i>k]}$,所以提出$k$,用线段树存下$\sum{x_i}$,分两段求和就好了。
注意一些小问题即可。调试1h一场空,不开long long见祖宗
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int N=100050; char rB[1<<21],*rS,*rT; inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,1<<21,stdin),rS==rT)?EOF:*rS++;} inline bool rdc(){ char c=gc(); while(c!='A'&&c!='B')c=gc(); return c=='B'; } inline int rdi(){ char c=gc(); while(c<48||c>57)c=gc(); int x=c&15; for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15); return x; } struct node{ int a,b; }a[N]; int b[N<<1],cnt[N<<3],ta[N],tb[N]; ll sum[N<<3],res[N]; inline bool cmp(node a,node b){return a.a+a.b<b.a+b.b;} void add(int o,int L,int R,int x){ ++cnt[o];sum[o]+=b[x]; if(L<R){ int lc=o<<1,rc=lc|1,M=L+R>>1; if(x<=M)add(lc,L,M,x); else add(rc,M+1,R,x); } } int kth(int o,int L,int R,int k){ if(L==R)return L; int lc=o<<1,rc=lc|1,M=L+R>>1; return k<=cnt[lc]?kth(lc,L,M,k):kth(rc,M+1,R,k-cnt[lc]); } int cot(int o,int L,int R,int x,int y){ //计算有多少个中位数要被加上/减去 if(x>y)return 0; if(x<=L&&y>=R)return cnt[o]; int lc=o<<1,rc=lc|1,M=L+R>>1,ans=0; if(x<=M)ans=cot(lc,L,M,x,y); if(y>M)ans+=cot(rc,M+1,R,x,y); return ans; } ll query(int o,int L,int R,int x,int y){ if(x>y)return 0ll; if(x<=L&&y>=R)return sum[o]; int lc=o<<1,rc=lc|1,M=L+R>>1; ll ans=0ll; if(x<=M)ans=query(lc,L,M,x,y); if(y>M)ans+=query(rc,M+1,R,x,y); return ans; } int main(){ int k=rdi(),n=rdi(),m=0,tot=0,i,x,y; ll ans=0ll,minn; bool t1,t2; for(i=1;i<=n;++i){ t1=rdc();x=rdi();t2=rdc();y=rdi(); if(t1^t2){b[++m]=a[++tot].a=x;b[++m]=a[tot].b=y;} else ans+=abs(y-x); } if(!tot){ //tot=0是个坑 printf("%lld",ans); return 0; } ans+=tot; sort(b+1,b+m+1); if(k==1){ for(i=1;i<=m;++i)ans+=abs(b[i]-b[tot]); printf("%lld",ans); }else{ m=unique(b+1,b+m+1)-b-1; sort(a+1,a+tot+1,cmp); for(i=1;i<=tot;++i){ add(1,1,m,ta[i]=lower_bound(b+1,b+m+1,a[i].a)-b);add(1,1,m,tb[i]=lower_bound(b+1,b+m+1,a[i].b)-b); x=kth(1,1,m,i); res[i]=query(1,1,m,x+1,m)-query(1,1,m,1,x-1)-(ll)(cot(1,1,m,x+1,m)-cot(1,1,m,1,x-1))*b[x]; } memset(cnt,0,sizeof(cnt));memset(sum,0,sizeof(sum)); for(minn=res[tot],i=tot;i;--i){ add(1,1,m,ta[i]);add(1,1,m,tb[i]); x=kth(1,1,m,tot-i+1); minn=min(minn,res[i-1]+query(1,1,m,x+1,m)-query(1,1,m,1,x-1)-(ll)(cot(1,1,m,x+1,m)-cot(1,1,m,1,x-1))*b[x]); } printf("%lld",ans+minn); } return 0; }