bzoj4071 [Apio2015]巴邻旁之桥 线段树

Description


一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 A 和区域 B。

每一块区域沿着河岸都建了恰好 1000000001 栋的建筑,每条岸边的建筑都从 0 编号到 1000000000。相邻的每对建筑相隔 1 个单位距离,河的宽度也是 1 个单位长度。区域 A 中的 i 号建筑物恰好与区域 B 中的 i 号建筑物隔河相对。
城市中有 N 个居民。第 i 个居民的房子在区域 Pi 的 Si 号建筑上,同时他的办公室坐落在 Qi 区域的 Ti 号建筑上。一个居民的房子和办公室可能分布在河的两岸,这样他就必须要搭乘船只才能从家中去往办公室,这种情况让很多人都觉得不方便。为了使居民们可以开车去工作,政府决定建造不超过 K 座横跨河流的大桥。
由于技术上的原因,每一座桥必须刚好连接河的两岸,桥梁必须严格垂直于河流,并且桥与桥之间不能相交。当政府建造最多 K 座桥之后,设 Di 表示第 i 个居民此时开车从家里到办公室的最短距离。请帮助政府建造桥梁,使得 D1+D2+⋯+DN 最小。

1<=K<=2
1≤N≤100000

Solution


一开始随意感受了一下发现k=1的时候是个凹函数,k=2的时候可以三分套三分,信仰一波就T了。但是三分有65pts哇

正解:当k=1时答案就是中位数,当k=2的时候枚举分界线,左半边的中位数和右半边的中位数就是答案,用两棵权值线段树支持插入、删除、求中位数

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
#define fi first
#define se second

typedef std:: pair <double,int> pair;
typedef long long LL;
const int N=200005;

struct segTree {
    int size[N*30],l[N*30],r[N*30],root,tot;
    LL sum[N*30];

    void modify(int &now,int tl,int tr,int x,int v) {
        if (!now) now=++tot;
        sum[now]+=x*v; size[now]+=v;
        if (tl==tr) return ;
        int mid=(tl+tr)>>1;
        if (x<=mid) modify(l[now],tl,mid,x,v);
        else modify(r[now],mid+1,tr,x,v);
    }

    LL query(int now,int tl,int tr,int x) {
        if (tr<=x) return (LL)x*size[now]-sum[now];
        if (x<=tl) return sum[now]-(LL)x*size[now];
        int mid=(tl+tr)>>1;
        return query(l[now],tl,mid,x)+query(r[now],mid+1,tr,x);
    }

    int kth(int now,int tl,int tr,int k) {
        if (tl==tr) return tl;
        int mid=(tl+tr)>>1;
        if (k<=size[l[now]]) return kth(l[now],tl,mid,k);
        else return kth(r[now],mid+1,tr,k-size[l[now]]);
    }
} t1,t2;

struct Q {int x,y;} q[N];

pair vec2[N];

LL vec1[N];
LL L[N],R[N];

LL myabs(LL x) {
    return (x<0)?(-x):(x);
}

void solve1(int n) {
    LL ans=0; int tot=0;
    rep(i,1,n) {
        char opt1[2],opt2[2];
        LL x1,x2; scanf("%s%lld%s%lld",&opt1,&x1,&opt2,&x2);
        if (opt1[0]==opt2[0]) ans+=myabs(x1-x2);
        else vec1[++tot]=x1,vec1[++tot]=x2,ans++;
    }
    std:: sort(vec1+1,vec1+tot+1);
    LL pos=vec1[(tot+1)/2];
    rep(i,1,tot) ans+=myabs(pos-vec1[i]);
    printf("%lld\n", ans);
}

LL calc() {
    int p1=t1.kth(t1.root,0,1000000000,(1+t1.size[t1.root])/2);
    int p2=t2.kth(t2.root,0,1000000000,(1+t2.size[t2.root])/2);
    return t1.query(t1.root,0,1000000000,p1)+t2.query(t2.root,0,1000000000,p2);
}

void solve2(int n) {
    LL pre=0; int tot=0;
    rep(i,1,n) {
        char opt1[2],opt2[2];
        scanf("%s%d%s%d",&opt1,&L[i],&opt2,&R[i]);
        if (L[i]>R[i]) std:: swap(L[i],R[i]);
        if (opt1[0]==opt2[0]) {
            pre+=myabs(L[i]-R[i]);
            i--,n--;
        } else {
            vec2[++tot]=pair(0.5*(L[i]+R[i]),i);
            pre++;
        }
    }
    std:: sort(vec2+1,vec2+tot+1);
    rep(i,1,tot) {
        t2.modify(t2.root,0,1000000000,L[vec2[i].se],1);
        t2.modify(t2.root,0,1000000000,R[vec2[i].se],1);
    }
    LL ans=calc()+pre;
    rep(i,1,tot-1) {
        t2.modify(t2.root,0,1000000000,L[vec2[i].se],-1);
        t2.modify(t2.root,0,1000000000,R[vec2[i].se],-1);
        t1.modify(t1.root,0,1000000000,L[vec2[i].se],1);
        t1.modify(t1.root,0,1000000000,R[vec2[i].se],1);
        ans=std:: min(ans,pre+calc());
    }
    printf("%lld\n", ans);
}

int main(void) {
    t1.root=t1.tot=t2.root=t2.tot=0;
    int k,n; scanf("%d%d",&k,&n);
    if (k==1) solve1(n);
    else solve2(n);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/80079888