洛谷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;
}
View Code

 

posted @ 2019-08-05 14:01  wangyuchen  阅读(151)  评论(0编辑  收藏  举报