Live2D

题解 [BJOI2017]开车

题目传送门

题目大意

\(n\)个汽车和\(n\)个加油站,坐标分别为\(a_{1,2,...,n}\)\(b_{1,2,...,n}\)。每辆汽车会到一个加油站,求出最小移动距离之和。有\(m\)次修改,每次将某辆汽车的坐标进行修改,求出修改后的最小移动距离之和。

\(n,m\le 5\times 10^4\)

思路

看到题解都写得比较繁杂,这里提供一种不是那么繁杂的方法。借鉴了Miracle的博客shadowice1984的题解

首先,不难看出对于某一条边,它的贡献为它的长度乘上\(|sum|\),其中,\(sum\)就是在它之前的汽车-在它之前的加油站。它的意义就是因为要一一对应,所以差的数量就需要通过移动填补,而移动就需要经过该边。

而我们的修改操作,相当于删掉一个点再加入一个点。考虑加入一个点,那我们就相当于把后面的点的\(sum+1\)。但是因为贡献里面带有绝对值,所以我们不能直接搞,对于这种问题我们一个常用的解决方法就是直接分块。对于某个块,我们可以按\(sum\)大小排序,二分找到分界点,然后分别考虑\(sum< 0\)\(sum\ge0\)的情况即可。删掉一个点同理。

于是,我们就可以在\(\Theta(n\sqrt n(\log \sqrt n))\)的时间复杂度内解决这个问题。

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std;

#define Abs(x) ((x)>=0?(x):-(x))
#define Int register int
#define ll long long
#define MAXN 200005
#define MAXM 455

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

int n,m,un,a[MAXN],b[MAXN],tmp[MAXN];

struct Query{
	int x,y;
}q[MAXN];

ll ans;
int siz,val[MAXN],sum[MAXN],bel[MAXN],ord[MAXN],col[MAXM],cor[MAXM],tag[MAXN],sval[MAXN];

bool cmp (int a,int b){return sum[a] < sum[b];}

void rebuild (int x){
	sort (ord + col[x],ord + cor[x] + 1,cmp);
	sval[col[x]] = val[ord[col[x]]];for (Int i = col[x] + 1;i <= cor[x];++ i) sval[i] = sval[i - 1] + val[ord[i]];
}

void init (){
	for (Int i = 1;i <= n;++ i) sum[a[i]] ++,sum[b[i]] --;
	for (Int i = 1;i <= un;++ i){
		if (i < un) val[i] = tmp[i + 1] - tmp[i];
		sum[i] += sum[i - 1],ans += 1ll * Abs (sum[i]) * val[i];
	}
	siz = ceil (sqrt (un));
	for (Int i = 1;i <= un;++ i){
		bel[i] = (i - 1) / siz + 1,ord[i] = i;
		if (!col[bel[i]]) col[bel[i]] = i;
		cor[bel[i]] = i;
	}
	for (Int i = 1;i <= bel[un];++ i) rebuild (i);
}

void ins (int x){
	for (Int i = x;i <= cor[bel[x]];++ i){
		ans += 1ll * val[i] * (sum[i] + tag[bel[x]] >= 0 ? 1 : -1);//注意:三目运算符优先级比加减乘除低 
		sum[i] ++;
	}
	rebuild (bel[x]);
	for (Int i = bel[x] + 1;i <= bel[un];++ i){
		int l = col[i],r = cor[i],res = -1;
		while (l <= r){
			int mid = (l + r) >> 1;
			if (sum[ord[mid]] + tag[i] >= 0) res = mid,r = mid - 1;
			else l = mid + 1;
		}
		if (res == -1) ans -= sval[cor[i]];
		else if (res == col[i]) ans += sval[cor[i]];
		else{
			ans -= sval[res - 1];
			ans += sval[cor[i]] - sval[res - 1];
		}
		tag[i] ++;
	}
}

void del (int x){
	for (Int i = x;i <= cor[bel[x]];++ i){
		ans += 1ll * val[i] * (sum[i] + tag[bel[x]] <= 0 ? 1 : -1);//注意:三目运算符优先级比加减乘除低 
		sum[i] --;
	}
	rebuild (bel[x]);
	for (Int i = bel[x] + 1;i <= bel[un];++ i){
		int l = col[i],r = cor[i],res = -1;
		while (l <= r){
			int mid = (l + r) >> 1;
			if (sum[ord[mid]] + tag[i] <= 0) res = mid,l = mid + 1;
			else r = mid - 1;
		}
		if (res == -1) ans -= sval[cor[i]];
		else if (res == cor[i]) ans += sval[cor[i]];
		else{
			ans += sval[res];
			ans -= sval[cor[i]] - sval[res];
		}
		tag[i] --;
	}
}

signed main(){
	read (n);
	for (Int i = 1;i <= n;++ i) read (a[i]),tmp[++ un] = a[i];
	for (Int i = 1;i <= n;++ i) read (b[i]),tmp[++ un] = b[i];
	read (m);for (Int i = 1;i <= m;++ i) read (q[i].x,q[i].y),tmp[++ un] = q[i].y;
	sort (tmp + 1,tmp + un + 1),un = unique (tmp + 1,tmp + un + 1) - tmp - 1;
	for (Int i = 1;i <= n;++ i) a[i] = lower_bound (tmp + 1,tmp + un + 1,a[i]) - tmp,b[i] = lower_bound (tmp + 1,tmp + un + 1,b[i]) - tmp;
	for (Int i = 1;i <= m;++ i) q[i].y = lower_bound (tmp + 1,tmp + un + 1,q[i].y) - tmp;
	init (),write (ans),putchar ('\n');
	for (Int i = 1,x,y;i <= m;++ i){
		x = q[i].x,y = q[i].y;
		del (a[x]),ins (a[x] = y);
		write (ans),putchar ('\n');
	}
	return 0;
}
posted @ 2020-07-24 14:26  Dark_Romance  阅读(121)  评论(0编辑  收藏  举报