• 【BZOJ4908】[BeiJing2017] 开车(分块)


    点此看题面

    大致题意:(n)辆车和(n)个加油站分别在(a_{1sim n},b_{1sim n})的位置,每个加油站只能为一辆车加油。每次修改一个(a_i),求所有车移动距离的最小值。

    前言

    一开始就想到死胡同里去,结果死活想不出来。。。

    点开题解第一眼便看到一个神奇的转化,然后这道题就变成大水题了。。。

    看来我还是太菜。

    转化

    显然可以发现,若把(a,b)分别从小到大排序,则答案就是(sum_{i=1}^n|a_i-b_i|)

    但修改(a_i)会导致大小顺序的改变,所以这个东西并不好维护。

    因此,这里就需要一个神奇的转化:

    对于第(i)段道路,假设它长度为(l_i),且在它左侧的车数和加油站数之差为(f_i),则答案就是(sum l_icdot|f_i|)

    因为相当于有(|f_i|)辆车必须经过这条路,使得车数和加油站数相等,从而相匹配。

    分块

    那么,现在的问题就是如何维护(f_i)了,而这简直不要太简单。

    我们离线先记录下所有有用的位置,然后离散化,就能把道路给分割开。

    然后发现,改变一个(a_i),其实就相当于有一段区间的道路左侧的车数全部加/减(1)

    考虑加/减(1)的修改对答案的影响:

    • (f_i>0),则(ans+=l_i/ans-=l_i)
    • (f_i=0),则(ans+=l_i/ans+=l_i)
    • (f_i<0),则(ans-=l_i/ans+=l_i)

    所以我们记下(f_i>0)的道路总长度与(f_i<0)道路总长度的差值。并开个(map),记下(f_i=k)的道路总长度,这样既能求出(f_i=0)的道路总长度,又可以实现差值的维护。

    于是,我们就可以用分块轻松解决此题了。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 50000
    #define V 150000
    #define GV(x) (lower_bound(dv+1,dv+dc+1,x)-dv)
    using namespace std;
    int n,m,a[N+5],b[N+5],id[N+5],p[N+5],dc,dv[V+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define pc(c) (C==E&&(clear(),0),*C++=c)
    		#define tn (x<<3)+(x<<1)
    		#define D isdigit(c=tc())
    		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
    	public:
    		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
    		Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
    		Tp I void writeln(Con Ty& x) {write(x),pc('
    ');}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    }F;
    class BlockSolver//分块
    {
    	private:
    		#define SV 400
    		int Bs,Bt,f[V+5],Bl[V+5],t[V+5],g[V+5];long long ans;map<int,int> s[SV+5];
    		I void BF(CI l,CI r,CI v)//暴力修改并重构块
    		{
    			RI i,p=Bl[l],L=(p-1)*Bs+1,R=p^Bt?p*Bs:dc;
    			for(i=L;i<=R;++i) ans-=1LL*abs(f[i]+=t[p])*(dv[i+1]-dv[i]);//删去原先贡献
    			for(i=l;i<=r;++i) f[i]+=v;for(i=L;i<=R;++i) ans+=1LL*abs(f[i])*(dv[i+1]-dv[i]);//修改并计算新贡献
    			for(t[p]=g[p]=0,s[p].clear(),i=L;i<=R;++i)//重构
    				f[i]&&(g[p]+=(f[i]>0?1:-1)*(dv[i+1]-dv[i])),s[p][f[i]]+=dv[i+1]-dv[i];
    		}
    	public:
    		I void Init(int *a,int *b)//初始化
    		{
    			RI i;for(i=1;i<=n;++i) ++f[a[i]],--f[b[i]];
    			for(i=1;i<=dc;++i) f[i]+=f[i-1],ans+=1LL*abs(f[i])*(dv[i+1]-dv[i]);F.writeln(ans);//初始答案
    			for(Bs=sqrt(dc),i=1;i<=dc;++i) Bl[i]=(i-1)/Bs+1,//初始化块信息
    				f[i]&&(g[Bl[i]]+=(f[i]>0?1:-1)*(dv[i+1]-dv[i])),s[Bl[i]][f[i]]+=dv[i+1]-dv[i];
    			Bt=Bl[dc];
    		}
    		I void Upt(CI l,CI r,CI v)//区间修改
    		{
    			if(Bl[l]==Bl[r]) {BF(l,r,v);goto End;}BF(l,Bl[l]*Bs,v),BF((Bl[r]-1)*Bs+1,r,v);//暴力修改散块
    			for(RI i=Bl[l]+1;i^Bl[r];++i)//打标记处理完整块
    				~v?(ans+=(g[i]+=s[i][-t[i]]),g[i]+=s[i][-(++t[i])])//加1
    				:(ans-=(g[i]-=s[i][-t[i]]),g[i]-=s[i][-(--t[i])]);//减1
    			End:F.writeln(ans);
    		}
    }B;
    int main()
    {
    	RI i;F.read(n);
    	for(i=1;i<=n;++i) F.read(a[i]),dv[++dc]=a[i];
    	for(i=1;i<=n;++i) F.read(b[i]),dv[++dc]=b[i];
    	for(F.read(m),i=1;i<=m;++i) F.read(id[i]),F.read(p[i]),dv[++dc]=p[i];
    	sort(dv+1,dv+dc+1),dc=unique(dv+1,dv+dc+1)-dv-1,dv[dc+1]=dv[dc];//离散化
    	for(i=1;i<=n;++i) a[i]=GV(a[i]),b[i]=GV(b[i]);for(i=1;i<=m;++i) p[i]=GV(p[i]);
    	for(B.Init(a,b),i=1;i<=m;++i) a[id[i]]^p[i]&&
    		(a[id[i]]<p[i]?B.Upt(a[id[i]],p[i]-1,-1):B.Upt(p[i],a[id[i]]-1,1),a[id[i]]=p[i]);
    	return F.clear(),0;
    }
    
  • 相关阅读:
    python批量安装包文件requirements.txt
    python→列表、元组、字典、集合对比整理表
    Python ddt+xlrd的使用
    input类型上传多个文件(selenium+Python)
    iframe切入切出问题,经常会碰到
    selenium+python 第一步启动火狐报错解决方案,已成功
    QTP从svn上载下来无法跑,解决
    函数可变参传值(python)
    关于python 函数参数
    Air Kiss
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4908.html
Copyright © 2020-2023  润新知