BZOJ4071 [APIO2015]八邻旁之桥

时间:2021-05-20 20:44:37

题意

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

每一块区域沿着河岸都建了恰好 \(1000000001\) 栋的建筑,每条岸边的建筑都从 \(0\) 编号到 \(1000000000\)。相邻的每对建筑相隔 \(1\) 个单位距离,河的宽度也是 \(1\) 个单位长度。区域 \(A\) 中的 \(i\) 号建筑物恰好与区域 \(B\) 中的 \(i\) 号建筑物隔河相对。

城市中有 \(N\) 个居民。第 \(i\) 个居民的房子在区域 \(P_i\)\(S_i\) 号建筑上,同时他的办公室坐落在 \(Q_i\) 区域的 \(T_i\) 号建筑上。一个居民的房子和办公室可能分布在河的两岸,这样他就必须要搭乘船只才能从家中去往办公室,这种情况让很多人都觉得不方便。为了使居民们可以开车去工作,*决定建造不超过 \(K\) 座横跨河流的大桥。

由于技术上的原因,每一座桥必须刚好连接河的两岸,桥梁必须严格垂直于河流,并且桥与桥之间不能相交。

当*建造最多 \(K\) 座桥之后,设 \(D_i\) 表示第 \(i\) 个居民此时开车从家里到办公室的最短距离。请帮助*建造桥梁,使得 \(D_1 + D_2 + \cdots + D_N\) 最小。

\(1 \leq K \leq 2 , 1 \leq N \leq 10^5\)

分析

参照租酥雨的题解。

这题看上去很不可做呀,但是很明显K≤2这个条件是非常重要的。

先考虑K=1怎么做。

首先,如果一个人的房子和办公室位于河的同一侧,那么这个人肯定是不受任何影响的,即他对答案的贡献永不变。我们先把这种人预处理了,然后剩下的全是必须要过河的。

那么现在有cnt个人要过河,第i个人要从Ai走到Bi,或者说,从Ai走到桥的位置pos,再从桥的位置pos走到Bi。说白了我们就是要求∑abs(Ai−pos)+abs(Bi−pos),可发现Ai与Bi其实无差别。

所以就把所有的Ai跟Bi放在一起排序,然后桥的位置就一定是排序后中位数的位置。暴力统计每个点到桥的距离,这样K=1就做完了。

现在来考虑K=2。

首先看这样一个结论:对每个人来说,他一定会走那座离(Ai+Bi)/2更近的桥。这个结论其实不需要证明,手玩一下即可。

那么我们把所有人按照(Ai+Bi)/2排序,那么一定是左边一部分人走左边的那座桥,右边的一部分人走右边的那座桥。

所以我们要对左右分别维护一个数据结构,支持快速插入、删除元素,并可以快速查询中位数、查询区间和。在这里splay和权值线段树都是不错的选择。

那么这题就做完啦。嗯哼。

我用函数式Treap实现,时间复杂度\(O(n \log n)\)

某神犇:好题之所以为好题,不仅要题好,而且数据要好。

那么这题的确是好题了,不仅有卡常数据,而且还有卡RE的小数据。

代码

开始我信心满满地交了几遍

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read()
{
    rg T data=0;
    rg int w=1;
    rg char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        data=data*10+ch-'0';
        ch=getchar();
    }
    return data*w;
}
template<class T>il T read(rg T&x)
{
    return x=read<T>();
}
typedef long long ll;
using std::cerr;
using std::endl;

co int N=1e5+100;
int root[2];
int tot;
namespace  T
{
    int ch[N<<2][2],siz[N<<2];
    ll val[N<<2],sum[N<<2];
    int pri[N<<2];
    
    il int newnode(rg ll v)
    {
        int x=++tot;
        ch[x][0]=ch[x][1]=0,siz[x]=1;
        val[x]=sum[x]=v;
        pri[x]=rand();
        return x;
    }
    
    il void pushup(rg int x)
    {
        siz[x]=siz[ch[x][0]]+1+siz[ch[x][1]];
        sum[x]=sum[ch[x][0]]+val[x]+sum[ch[x][1]];
    }
    
    il void split(rg int x,rg ll v,rg int&l,rg int&r)
    {
        if(!x)
        {
            l=r=0;
            return;
        }
        if(val[x]<=v)
        {
            l=x;
            split(ch[l][1],v,ch[l][1],r);
            pushup(l);
        }
        else
        {
            r=x;
            split(ch[r][0],v,l,ch[r][0]);
            pushup(r);
        }
    }
    
    il int merge(rg int x,rg int y)
    {
        if(!x||!y)
            return x+y;
        if(pri[x]>pri[y])
        {
            ch[x][1]=merge(ch[x][1],y);
            pushup(x);
            return x;
        }
        else
        {
            ch[y][0]=merge(x,ch[y][0]);
            pushup(y);
            return y;
        }
    }
    
    il void insert(rg int&t,rg int z)
    {
        rg int x,y;
        split(t,val[z],x,y);
        t=merge(x,merge(z,y));
    }
    
    il int erase(rg int&t,rg ll v)
    {
        rg int x,y,z;
        split(t,v-1,x,y);
        split(y,v,y,z);
        rg int res=y;
        y=merge(ch[y][0],ch[y][1]);
        t=merge(x,merge(y,z));
        ch[res][0]=ch[res][1]=0;
        pushup(res);
        return res;
    }
    
    il ll kth(rg int&t,rg int k)
    {
        if(k==siz[ch[t][0]]+1)
            return val[t];
        if(k<=siz[ch[t][0]])
            return kth(ch[t][0],k);
        else
            return kth(ch[t][1],k-siz[ch[t][0]]-1);
    }
    
    il ll query(rg int&t)
    {
        if(!t)
            return 0;
        rg ll v=kth(t,(siz[t]+1)/2);
        rg int x,y;
        split(t,v,x,y);
        rg ll res=v*siz[x]-sum[x]+sum[y]-v*siz[y];
        t=merge(x,y);
        return res;
    }
}
using namespace T;
using namespace std;

struct node
{
    ll s,t;
    
    il bool operator<(rg co node&nd)co
    {
        return s+t<nd.s+nd.t;
    }
}x[N];

int main()
{
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    srand(20030506);
    rg int k,n;
    read(k),read(n);
    rg ll ans=0;
    for(rg int i=1;i<=n;++i)
    {
        rg char p[2],q[2];
        rg ll s,t;
        scanf("%s %lld %s %lld",p,&s,q,&t);
        if(p[0]==q[0])
        {
            ans+=abs(s-t);
            --n,--i;
            continue;
        }
        x[i].s=s,x[i].t=t;
    }
    ans+=n;
    sort(x+1,x+n+1);
    if(k==1)
    {
        vector<int>v;
        for(rg int i=1;i<=n;++i)
        {
            v.push_back(x[i].s);
            v.push_back(x[i].t);
        }
        sort(v.begin(),v.end());
        rg ll mid=v[(v.size()+1)/2-1];
        for(rg int i=0;i<v.size();++i)
            ans+=abs(mid-v[i]); 
        printf("%lld\n",ans);
        return 0;
    }
    for(rg int i=1;i<=n;++i)
    {
        insert(root[0],newnode(x[i].s));
        insert(root[0],newnode(x[i].t));
    }
    rg ll sum=query(root[0]);
    for(rg int i=n;i>=1;--i)
    {
        insert(root[1],erase(root[0],x[i].s));
        insert(root[1],erase(root[0],x[i].t));
        sum=min(sum,query(root[0])+query(root[1]));
    }
    printf("%lld\n",ans+sum);
    return 0;
}

无论怎么卡常数都要TLE。

然后看别人的题解发现只需要插入维护个sum数组记录就行了,不用删除。

接着我的代码的确没有TLE了,然而RE了两个点。

发现是这组数据:

1 1
B 457748887 B 796098674

于是又改,终于AC了。

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read()
{
    rg T data=0;
    rg int w=1;
    rg char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        data=data*10+ch-'0';
        ch=getchar();
    }
    return data*w;
}
template<class T>il T read(rg T&x)
{
    return x=read<T>();
}
typedef long long ll;
using std::cerr;
using std::endl;

co int N=2e5;
int root[2];
int tot;
namespace  T
{
    int ch[N<<2][2],siz[N<<2];
    ll val[N<<2],sum[N<<2];
    int pri[N<<2];
    
    il int newnode(rg ll v)
    {
        int x=++tot;
        ch[x][0]=ch[x][1]=0,siz[x]=1;
        val[x]=sum[x]=v;
        pri[x]=rand();
        return x;
    }
    
    il void pushup(rg int x)
    {
        siz[x]=siz[ch[x][0]]+1+siz[ch[x][1]];
        sum[x]=sum[ch[x][0]]+val[x]+sum[ch[x][1]];
    }
    
    il void split(rg int x,rg ll v,rg int&l,rg int&r)
    {
        if(!x)
        {
            l=r=0;
            return;
        }
        if(val[x]<=v)
        {
            l=x;
            split(ch[l][1],v,ch[l][1],r);
            pushup(l);
        }
        else
        {
            r=x;
            split(ch[r][0],v,l,ch[r][0]);
            pushup(r);
        }
    }
    
    il int merge(rg int x,rg int y)
    {
        if(!x||!y)
            return x+y;
        if(pri[x]>pri[y])
        {
            ch[x][1]=merge(ch[x][1],y);
            pushup(x);
            return x;
        }
        else
        {
            ch[y][0]=merge(x,ch[y][0]);
            pushup(y);
            return y;
        }
    }
    
    il void insert(rg int&t,rg int z)
    {
        rg int x,y;
        split(t,val[z],x,y);
        t=merge(x,merge(z,y));
    }
    
    il int erase(rg int&t,rg ll v)
    {
        rg int x,y,z;
        split(t,v-1,x,y);
        split(y,v,y,z);
        rg int res=y;
        y=merge(ch[y][0],ch[y][1]);
        t=merge(x,merge(y,z));
        ch[res][0]=ch[res][1]=0;
        pushup(res);
        return res;
    }
    
    il ll kth(rg int&t,rg int k)
    {
        if(k==siz[ch[t][0]]+1)
            return val[t];
        if(k<=siz[ch[t][0]])
            return kth(ch[t][0],k);
        else
            return kth(ch[t][1],k-siz[ch[t][0]]-1);
    }
    
    il ll query(rg int&t)
    {
        if(!t)
            return 0;
        rg ll v=kth(t,(siz[t]+1)/2);
        rg int x,y;
        split(t,v,x,y);
        rg ll res=v*siz[x]-sum[x]+sum[y]-v*siz[y];
        t=merge(x,y);
        return res;
    }
}
using namespace T;
using namespace std;

struct node
{
    ll s,t;
    
    il bool operator<(rg co node&nd)co
    {
        return s+t<nd.s+nd.t;
    }
}x[N];

ll sum[N];

int main()
{
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    srand(20030506);
    rg int k,n;
    read(k),read(n);
    rg ll ans=0;
    for(rg int i=1;i<=n;++i)
    {
        rg char p[2],q[2];
        rg ll s,t;
        scanf("%s %lld %s %lld",p,&s,q,&t);
        if(p[0]==q[0])
        {
            ans+=abs(s-t);
            --n,--i;
            continue;
        }
        x[i].s=s,x[i].t=t;
    }
    ans+=n;
    sort(x+1,x+n+1);
    if(k==1)
    {
        if(!n)
        {
            printf("%lld\n",ans);
            return 0;
        }
        vector<int>v;
        for(rg int i=1;i<=n;++i)
        {
            v.push_back(x[i].s);
            v.push_back(x[i].t);
        }
        sort(v.begin(),v.end());
        rg ll mid=v[(v.size()+1)/2-1];
        for(rg int i=0;i<v.size();++i)
            ans+=abs(mid-v[i]); 
        printf("%lld\n",ans);
        return 0;
    }
    for(rg int i=1;i<=n;++i)
    {
        insert(root[0],newnode(x[i].s));
        insert(root[0],newnode(x[i].t));
        ::sum[i]=query(root[0]);
    }
    ll sol=::sum[n];
    for(rg int i=n;i>=1;--i)
    {
        insert(root[1],newnode(x[i].s));
        insert(root[1],newnode(x[i].t));
        sol=min(sol,::sum[i-1]+query(root[1]));
    }
    printf("%lld\n",ans+sol);
    return 0;
}