P3644 [APIO2015] 巴邻旁之桥

P3644 [APIO2015] 巴邻旁之桥

题目描述

一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 \(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\) 最小。

数据范围

所有数据都保证:\(P_i\)\(Q_i\) 为字符 “A” 和 “B” 中的一个, \(0 \leq S_i, T_i \leq 1000000000\),同一栋建筑内可能有超过 \(1\) 间房子或办公室(或二者的组合,即房子或办公室的数量同时大于等于 \(1\))。
\(1\le K \le 2\)
\(1 \leq N \leq 100000\)

Solution:

问题转化:

首先我们先来处理一下路径,我们发现河的两岸这一限制并不重要,如果一个人上班不过桥,贡献直接就是 \(|T-S|\)。如果他过桥,假设桥建在 \(x\) ,那么贡献就是 \(|x-S|+|T-x|+1\)

所以现在题目变为了:在一条线段上有一些点,你需要确定一个或者两个关键点使得所有点到最近的关键点的距离 \(D_i\) 的总和最小。

首先我们发现 \(K=1\) 时的做法很显然,我们只需要选这些点的中位数就好了。

对于K=2:

回顾一下贡献方程:
\(|x-S|+|T-x|\)

我们现在假设 \(S\le T\):

\[w= \begin{cases} T-S \ | \ x\in[S,T]\\ T-S+2(S-x) \ \ x<S \\ T-S+2(x-T) \ \ T<x \\ \end{cases} \]

整理得:

\[w= \begin{cases} T-S \ | \ x\in[S,T]\\ 2(mid-x) \ \ x<S \\ 2(x-mid) \ \ T<x \\ \end{cases} \]

我们发现,一个任务 (S,T) 会去到哪个关键点是由其中点 \(mid\) 到关键点 \(x\) 的距离决定的。所以我们只需要按照中点的坐标排序,就可以枚举一个分割点 \(pos\) 保证排序后标号在 \([1,pos]\) 的点走左边的桥 \((pos,tot]\) 的点走右边的桥。

所以我们需要动态维护一个数据结构,支持在线插入或删除一个点,区间求和,求中位数。

至于答案统计:

我们对每线段树找出中位数 \(mid\) 之后,查询 \([1,mid]\) 上的和 \(sum\) ,点的个数 \(cnt\) .该部分贡献为 \(-sum+cnt\times mid\).\((mid,inf]\) 部分类似。

值得注意的是我们应该求出区间上点的个数 \(cnt\) 。以统计答案,而非简单的用整颗线段树上点的总数的一半。

Code:

#include<bits/stdc++.h>
#define ll long long
const int N=2e5+5;
const int inf=1e9+1;
using namespace std;
struct Segment_Tree{
    int cnt,rt;
    struct Tree{
        int ls,rs,cnt;
        ll sum;
    }t[N*32];
    void insert(int &x,int l,int r,int pos,int k)
    {
        t[x=(x ? x : ++cnt)].cnt+=k;t[x].sum+=k*pos;
        if(l==r)return;int mid=l+r>>1;
        if(pos<=mid)insert(t[x].ls,l,mid,pos,k);
        if(mid<pos)insert(t[x].rs,mid+1,r,pos,k);
    }
    int query_mid(int x,int l,int r,int k)
    {
        if(!x||!t[x].cnt)return 0;
        if(l==r)return l;int mid=l+r>>1;
        if(t[t[x].ls].cnt<k)return query_mid(t[x].rs,mid+1,r,k-t[t[x].ls].cnt);
        else return query_mid(t[x].ls,l,mid,k);
    }
    ll query_sum(int x,int l,int r,int L,int R)
    {
        if(!x||!t[x].sum)return 0;
        if(L<=l&&r<=R)return t[x].sum;
        int mid=l+r>>1;ll res=0;
        if(L<=mid)res+=query_sum(t[x].ls,l,mid,L,R);
        if(mid<R)res+=query_sum(t[x].rs,mid+1,r,L,R);
        return res;
    }
    ll query_cnt(int x,int l,int r,int L,int R)
    {
        if(!x||!t[x].sum)return 0;
        if(L<=l&&r<=R)return t[x].cnt;
        int mid=l+r>>1;ll res=0;
        if(L<=mid)res+=query_cnt(t[x].ls,l,mid,L,R);
        if(mid<R)res+=query_cnt(t[x].rs,mid+1,r,L,R);
        return res;
    }
    int Mid()
    {
        int k=t[rt].cnt>>1;
        return query_mid(rt,1,inf,k);
    }
    ll calc()
    {
        int mid=Mid(),k=t[rt].cnt>>1;ll res=0,tmp,cnt;
        cnt=query_cnt(rt,1,inf,1,mid);tmp=-query_sum(rt,1,inf,1,mid)+1ll*cnt*mid;
        res+=tmp;
        cnt=query_cnt(rt,1,inf,mid+1,inf);tmp=query_sum(rt,1,inf,mid+1,inf)-1ll*cnt*mid;
        res+=tmp;
        return res;
    }
}T1,T2;
struct task{
    int l,r;
    bool operator<(const task &t)const{
        return l+r<t.l+t.r;
    }
}q[N];
int n,k,tot;
char c[2];
ll ans,tmp;
void work()
{
    for(int i=1,x,y;i<=n;i++)
    {
        cin>>c[0]>>x>>c[1]>>y;
        if(x>y)swap(x,y);
        if(c[0]==c[1])ans+=y-x;
        else{q[++tot]={++x,++y};}
    }
    sort(q+1,q+1+tot);ans+=tot;
    for(int i=1;i<=tot;i++)
    {
        int l=q[i].l,r=q[i].r;
        T1.insert(T1.rt,1,inf,l,1);
        T1.insert(T1.rt,1,inf,r,1);
    }
    tmp=T1.calc();
    if(k==1){cout<<ans+tmp;return;}
    for(int i=tot;i>=1;i--)
    {
        int l=q[i].l,r=q[i].r;
        T1.insert(T1.rt,1,inf,l,-1);T2.insert(T2.rt,1,inf,l,1);
        T1.insert(T1.rt,1,inf,r,-1);T2.insert(T2.rt,1,inf,r,1);
        ll res=T1.calc()+T2.calc();
        tmp=min(tmp,res);
    }
    ans+=tmp;
    cout<<ans;
}
int main()
{
    //freopen("P3644.in","r",stdin);freopen("P3644.out","w",stdout);
    ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>k>>n;
    work();
    return 0;
}
posted @ 2025-02-19 14:14  liuboom  阅读(16)  评论(0)    收藏  举报