BZOJ-4071: [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 最小。k<=2 n<=10^5

对于k=1的情况,代价为桥到所有Ai,Bi不同侧的Ai,Bi的距离,显然选择Ai,Bi的中位数建桥。对于k=2的情况,对于Ai,Bi,设桥的位置为p,那么代价为:
1.|Ai-Bi| 【p在二者中间】 2.|Ai+Bi-2*p|【p在二者同侧】
发现第二种等价到p的距离的二倍,那么对于第二种情况,每个Ai,Bi应当选择离二者中点近的桥,发现这个结论对于一情况也成立,尽可能的选择了在A,B之间的桥。
那么对于所有的Ai,Bi,我们对于它的中点排序,那么这个序列某个前缀是选择一个桥的,其余的选择另一个桥。转化为两个k=1的问题。枚举前缀,用平衡树SBT维护两段的代价即可(寻找中位数,加上比中位数大的,减去比其小的)。

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 101000 
typedef long long ll;
ll ans;
int n,m;
inline int Abs(int x){return x>0 ? x : -x;}
struct node
{
    int l,r;
}a[N];
bool cmp(node x,node y){return x.l+x.r<y.l+y.r;}
queue<int>de;
int tot,rot1,rot2,siz1,siz2;
struct SBT
{
    int ls,rs,siz,w;
    ll sum;
}t[N<<1];
inline void newSBT(int &x)
{
    if(de.size()) x=de.front(),de.pop();
    else x=++tot;
}
inline void zig(int &x)
{
    int l=t[x].ls;
    t[x].ls=t[l].rs; t[l].rs=x;
    t[l].siz=t[x].siz; t[l].sum=t[x].sum;
    t[x].siz=t[t[x].ls].siz+t[t[x].rs].siz+1;
    t[x].sum=t[t[x].ls].sum+t[t[x].rs].sum+t[x].w;
    x=l;
}
inline void zag(int &x)
{
    int r=t[x].rs;
    t[x].rs=t[r].ls; t[r].ls=x;
    t[r].siz=t[x].siz; t[r].sum=t[x].sum;
    t[x].siz=t[t[x].ls].siz+t[t[x].rs].siz+1;
    t[x].sum=t[t[x].ls].sum+t[t[x].rs].sum+t[x].w;
    x=r;
}
void maintain(int &x,bool fl)
{
    if(!fl)
    {
        if(t[t[t[x].ls].ls].siz>t[t[x].rs].siz) zig(x);
        else if(t[t[t[x].ls].rs].siz>t[t[x].rs].siz) zag(t[x].ls),zig(x);
        else return ;
    }
    else
    {
        if(t[t[t[x].rs].rs].siz>t[t[x].ls].siz) zag(x);
        else if(t[t[t[x].rs].ls].siz>t[t[x].ls].siz) zig(t[x].rs),zag(x);
        else return ;
    }
    maintain(t[x].ls,0); maintain(t[x].rs,1);
    maintain(x,0); maintain(x,1);
}
void insert(int &k,int x)
{
    if(!k)
    {
        newSBT(k),t[k].w=t[k].sum=x,t[k].siz=1;
        return ;
    }
    t[k].siz++; t[k].sum+=x;
    if(x<t[k].w) insert(t[k].ls,x);
    else insert(t[k].rs,x);
    maintain(k,x>=t[k].w);
}
void del(int &k,int x)
{
    t[k].siz--; t[k].sum-=x;
    if(x<t[k].w) del(t[k].ls,x);
    else if(x>t[k].w) del(t[k].rs,x);
    else
    {
        if(!t[k].ls||!t[k].rs)
        {
            int p=t[k].ls|t[k].rs;//
            t[k].ls=t[k].rs=0;
            de.push(k); k=p;
        }
        else
        {
            int p=t[k].ls; while(t[p].rs) p=t[p].rs;
            t[k].w=t[p].w; int i=t[k].ls,las=k;
            while(i!=p) t[i].siz--,t[i].sum-=t[p].w,las=i,i=t[i].rs;
            if(p==t[las].ls) t[las].ls=t[p].ls;
            else t[las].rs=t[p].ls;
            t[p].ls=t[p].rs=0; de.push(p);
        }
    }
}
inline ll query(int x,int y)
{
    ll re=0;
    while(x)
    {
        int tmp=t[t[x].ls].siz;
        if(y<=tmp)
        {
            re+=t[t[x].rs].sum+t[x].w;
            x=t[x].ls;
        }
        else
        {
            y-=(tmp+1);
            if(!y) return re-t[x].w-t[t[x].ls].sum+t[t[x].rs].sum;
            re-=t[t[x].ls].sum+t[x].w; x=t[x].rs;
        }
    }
    return 0;
}
int main()
{
    scanf("%d%d",&m,&n);
    int i=1; char s1[8],s2[8];
    while(i<=n)
    {
        scanf("%s%d%s%d",s1,&a[i].l,s2,&a[i].r);
        if(s1[0]==s2[0]) ans+=Abs(a[i].l-a[i].r),n--;
        else
        { 
            if(a[i].l>a[i].r) swap(a[i].l,a[i].r);
            ++i;
        }
    }
    sort(a+1,a+n+1,cmp); ll tmp=0x3f3f3f3f3f3f3f3fll;
    for(int i=1;i<=n;++i) insert(rot2,a[i].l),insert(rot2,a[i].r);
    siz2=(n<<1);
    tmp=min(tmp,query(rot2,siz2>>1));
    if(m==1) ans+=tmp;
    else
    {
        for(int i=1;i<n;++i)
        {
            del(rot2,a[i].l); del(rot2,a[i].r); siz2-=2;
            insert(rot1,a[i].l); insert(rot1,a[i].r); siz1+=2;
            tmp=min(tmp,query(rot1,siz1>>1)+query(rot2,siz2>>1));
        }
        ans+=tmp;
    }
    printf("%lld",ans+n);
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注