zoukankan      html  css  js  c++  java
  • [bzoj4908][BeiJing2017]开车

    来自FallDream的博客,未经允许,请勿转载,谢谢。


    你有n辆车,分别a1, a2, ..., an位置和n个加油站,分别在b1, b2, ... ,bn 。每个加油站只能支持一辆车的加油,所以你要把这些车开到不同的加油站加油。一个车从x位置开到y位置的代价为 |x-y| ,问如何安排车辆,使得代价之和最小。同时你有q个操作,每次操作会修改第i辆车的位置到x,你要回答每次修改操作之后最优安排方案的总代价。
     
    n,m<=50000
     
    首先排序之后一一匹配肯定是最优的。
    把点离散之后,车看作1,加油站看作-1,求前缀和。对于离散后的每个点,如果它的前缀和不为0,那么显然有些车/加油站不能匹配,产生的代价是前缀和的绝对值乘以这个点到下个点的距离。
    考虑分块维护这东西。对离散的点分根号个块,每一块都自己做一遍,处理出这个块在块之前的点前缀和取不同的值时会产生的代价。由于一个块自己的前缀和不超过根号n,所以只处理前缀和在正负根号n以内的即可,大于根号n或者小于负根号n的,额外代价加入的值相同。处理这个代价可以差分再差分,最后前缀和再前缀和,具体实现可以见代码,处理一个块复杂度$O(sqrt{n})$。
    然后对于修改操作,中间的整块移动一下重新统计答案,两遍的块重构即可。
    复杂度$O(nsqrt{n})$
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #define getchar() (*S++)
    #define pa pair<int,int>
    #define ll long long
    #define MN 50000
    #define MB 400
    char B[1<<26],*S=B;
    using namespace std;
    inline int read()
    {
        int x = 0 , f = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x * f;
    }
    long long Ans,ans[MB+5],F[MB+5][MB*2+5],f[MB+5][MB*2+5];
    int n,m,l[MN*3+5],cnt=0,pos[MB+5],a[MN+5],block[MN*3+5],v[MN*3+5];
    int s[MN*3+5],b[MN+5],tot=1,size;
    struct ques{int x,y;}q[MN+5];
    inline int abs(int x){return x<0?-x:x;}
    void Move(int x,int y)
    {
        Ans-=ans[x];
        if(abs(y)<=MB) Ans+=(ans[x]=F[x][MB+y]);
        else
        {
            if(y>MB) Ans+=(ans[x]=F[x][MB<<1]+1LL*(y-MB)*f[x][MB<<1]);
            if(y<MB) Ans+=(ans[x]=F[x][0]+1LL*(MB-y)*f[x][0]);
        }
    }
    
    void Build(int x)
    {
        int W=0;
        for(int i=0;i<=MB<<1;++i) f[x][i]=F[x][i]=0;
        for(int i=(x-1)*size+1;block[i]==x;++i)
        {
            F[x][MB]+=1LL*v[i]*abs(W+=s[i]);
            if(!W)  f[x][1+MB]+=v[i],f[x][MB-1]+=v[i];
            if(W>0) f[x][MB+1]+=v[i],W<MB?(f[x][MB-W-1]+=v[i]<<1):0,f[x][MB-1]-=v[i];
            if(W<0) f[x][MB+1]-=v[i],f[x][MB-W+1]+=v[i]<<1,f[x][MB-1]+=v[i];
        }
        for(int i=1;i<=MB;++i) 
            F[x][MB+i]=F[x][MB+i-1]+(f[x][MB+i]+=f[x][MB+i-1]),
            F[x][MB-i]=F[x][MB-i+1]+(f[x][MB-i]+=f[x][MB-i+1]);                
        Move(x,pos[x]);
    }
    
    int main()
    {
        fread(B,1,1<<26,stdin);
        n=read();
        for(int i=1;i<=n;++i) a[i]=l[++cnt]=read();
        for(int i=1;i<=n;++i) b[i]=l[++cnt]=read();
        m=read();
        for(int i=1;i<=m;++i) q[i].x=read(),q[i].y=l[++cnt]=read();
        sort(l+1,l+cnt+1);
        for(int i=2;i<=cnt;++i)    if(l[i]!=l[i-1]) l[++tot]=l[i];
        for(int i=1;i<tot;++i) v[i]=l[i+1]-l[i];
        size=sqrt(tot);
        for(int i=1;i<=n;++i) 
            a[i]=lower_bound(l+1,l+tot+1,a[i])-l,
            b[i]=lower_bound(l+1,l+tot+1,b[i])-l,
            ++s[a[i]],--s[b[i]];
        for(int i=1;i<=tot;++i) block[i]=(i-1)/size+1;
        for(int i=1,w=0;i<=block[tot];++i) 
        {
            pos[i]=w;Build(i);
            for(int j=(i-1)*size+1;block[j]==i;++j) w+=s[j];
        }
        printf("%lld
    ",Ans);
        for(int i=1;i<=m;++i)
        {
            q[i].y=lower_bound(l+1,l+tot+1,q[i].y)-l; 
            if(q[i].y!=a[q[i].x])
            {                
                --s[a[q[i].x]];++s[q[i].y];
                if(block[q[i].y]==block[a[q[i].x]])
                    Build(block[q[i].y]);
                else
                {
                    int From=min(block[a[q[i].x]],block[q[i].y]),
                          To=max(block[a[q[i].x]],block[q[i].y]);
                    for(int j=From+1;j<To;++j) 
                        Move(j,pos[j]+=(q[i].y>a[q[i].x]?-1:1));
                    if(a[q[i].x]>q[i].y) ++pos[block[a[q[i].x]]];
                    else --pos[block[q[i].y]];
                    Build(block[q[i].y]);Build(block[a[q[i].x]]);
                }
            }
            a[q[i].x]=q[i].y;printf("%lld
    ",Ans);
        }
        return 0;
    }
  • 相关阅读:
    Java自学笔记(21):【IO】数据流,标准输入输出
    makefile 学习笔记
    tensorflow 环境搭建
    matlab
    【转】MATLAB各种矩阵生成函数
    leetcode刷题收获
    leetcode 15. 3Sum
    STL 记录
    leetcode 服务器环境
    visual studio 2017 使用笔记
  • 原文地址:https://www.cnblogs.com/FallDream/p/bzoj4908.html
Copyright © 2011-2022 走看看