【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;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4908.html