【BJOI2017】开车

分块

题目大意:

有 $n$ 辆车和 $n$ 个加油站在数轴上,车从点 $a$ 开到 $b$ 要走的距离为 $|a-b|$。

现在要求一辆车开到一个加油站里,一个加油站只能给一辆车加油。问车要开的最少的总距离是多少。

有 $q$ 次操作,每次会改变一辆车的位置,每次改变完后需要再次回答这个问题。

题解:

有一个显然的贪心,每个加油站和最早的车(如果之前有未匹配的车)匹配,每辆车和最早的加油站(如果之前有未匹配的加油站)匹配。

换个思路,考虑将点离散化,然后考虑维护相邻两个点之间的这段距离被经过的次数。

我们令车为 $1$,加油站为 $-1$,设 $f_i$ 表示前 $i$ 个位置的前缀和,那么位置 $i$ 到 $i+1$ 这条边被经过的次数,就是 $|f_i|$,即未匹配的车或加油站数。

那么修改一辆车的位置,相当于区间加 $1$ 或者区间减 $1$。

由于有这个绝对值的限制,比较难用一些数据结构可以。

考虑数列分块,然后每个块里按照 $f_i$ 进行排序,那么这个绝对值,相当于块内,前一半是负贡献,后一半是正的贡献。

然后考虑区间加 $1$,减 $1$ 操作。

边角暴力修改,重新排序。

中间的整块部分,有一部分会加上贡献,另一部分会减去贡献。我们二分这个临界点,然后用前缀和累加贡献即可。

时间复杂度 $O(q\sqrt n\log n)$。足以通过本题。

实际上可以做到 $O(q\sqrt n)$。具体是暴力修改时使用基数排序代替sort,整块部分,将 $f_i$ 相同的缩起来,那么区间加 $1$ 或减 $1$,都最多使临界点的位置改变 $1$。这样就把两个部分的 $\log$ 都降下来了。

代码写的还是带 $\log$ 的。

Code:

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<vector>
#define bel(x)((x-1)/siz+1)
using namespace std;
typedef long long LL;
typedef pair<int,int>PII;
const int N=150050,siz=400;
vector<int>vec;
int m,n,q,A[N],B[N],L[404],R[404],blocks;
PII a[N];
struct Mov{
    int id,x;
}que[N];
inline int find(int x){return lower_bound(vec.begin(),vec.end(),x)-vec.begin();}
struct Block{
    PII a[404],b[404];int tag;
    int prs[404];
    LL ans;
    int L,R,len;
    inline void re_ans(){
        ans=0;
        for(int i=1;i<=len;++i)
        ans=ans+(LL)abs(b[i].first+tag)*b[i].second,prs[i]=prs[i-1]+b[i].second;
    }
    void init(int l,int r){
        L=l,R=r,len=r-l+1;tag=0;
        for(int i=l;i<=r;++i)
        a[i-l+1]=b[i-l+1]=::a[i];
        sort(b+1,b+len+1);
        re_ans();
    }
    void modify(int l,int r,int x){
        for(int i=l;i<=r;++i)
        a[i-L+1].first+=x;
        for(int i=1;i<=len;++i)b[i]=a[i];
        sort(b+1,b+len+1);
        re_ans();
    }
    void modify(int x){
        if(x==1){
            int id=lower_bound(b+1,b+len+1,make_pair(-tag,0))-b-1;
            ans-=prs[id];
            ans+=prs[len]-prs[id];
        }else
        if(x==-1){
            int id=lower_bound(b+1,b+len+1,make_pair(-tag+1,0))-b-1;
            ans+=prs[id];
            ans-=prs[len]-prs[id];
        }
        tag+=x;
    }
}G[404];
void modify(int l,int r,int x){
    const int bL=bel(l),bR=bel(r);
    if(bL==bR)
    G[bL].modify(l,r,x);else{
        G[bL].modify(l,R[bL],x);
        G[bR].modify(L[bR],r,x);
        for(int i=bL+1;i<bR;++i)
        G[i].modify(x);
    }
}
int main(){
    vec.push_back(-1);
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>m;
    for(int i=1;i<=m;++i)cin>>A[i],vec.push_back(A[i]);
    for(int i=1;i<=m;++i)cin>>B[i],vec.push_back(B[i]);
    cin>>q;
    for(int i=1;i<=q;++i)
    cin>>que[i].id>>que[i].x,vec.push_back(que[i].x);
    sort(vec.begin(),vec.end());
    vec.erase(unique(vec.begin(),vec.end()),vec.end());
    n=vec.size()-1;
    blocks=bel(n);
    for(int i=1;i<=blocks;++i)L[i]=R[i-1]+1,R[i]=i*siz;R[blocks]=n;
    for(int i=1;i<=m;++i)
    ++a[A[i]=find(A[i])].first,--a[find(B[i])].first;
    for(int i=1;i<=n;++i)
    a[i].first+=a[i-1].first,a[i].second=i==n?0:vec[i+1]-vec[i];
    for(int i=1;i<=blocks;++i)
    G[i].init(L[i],R[i]);
    LL ans=0;
    for(int i=1;i<=blocks;++i)ans+=G[i].ans;
    cout<<ans<<'\n';
    for(int i=1;i<=q;++i){
        int id=que[i].id,x=find(que[i].x),lst=A[id];
        if(lst<x)modify(lst,x-1,-1);else
        if(lst>x)modify(x,lst-1,1);
        A[id]=x;
        ans=0;
        for(int i=1;i<=blocks;++i)ans+=G[i].ans;
        cout<<ans<<'\n';
    }
    return 0;
}