【USACO 2018 February Silver】Teleportation 题解

简化の题目:

\(\sum\limits_{i=1}^n{\min\left\{|a_i-b_i|,|a_i|+|y-b_i|\right\}}\) 的最小值

思路

对每一个 \(y\) ,考虑 \(O(1)\) 计算答案。

我们先计算出 \(\sum|a_i-b_i|\) ,把答案转换成“节省”的最大值。

对于一组 \(a_i,b_i\) ,如果其经过传送门,那么传送门的终点一定在以 \(b_i\) 为中心点的一段区间上。同时,能节省的路程以 \(b_i\) 为中心向两边减少,也就是这样:

Inkedfig_teleport_silver_feb18_LI.jpg

接下来,我们只要求出 \(b_i\) 处节省了多少,就能求出左端点和右端点。

有两种情况

  1. \(|a_i|\ge|a_i-b_i|\) 没有节省,去到传送门的距离大于等于两点距离。

  2. \(|a_i|<|a_i-b_i|\) 节省了 \(|a_i-b_i|-|a_i|\) 即两点距离减去到传送门的距离。

如果是第二种情况,那么左端点就为:\(b_i-|a_i-b_i|+|a_i|\)

右端点为:\(b_i+|a_i-b_i|-|a|\)

就是中心点加减节省了多少

USACO的题解分成了两种情况,其实就是把绝对值拆开了。

得到左中右端点之后,把左端点的斜率减1,中间点的斜率加2(想想为什么),右端点的斜率减1。统计答案时用差分的方法,得到当前点的“节省值”比上一个点增加了多少,然后统计答案。

实现

在更改斜率时,使用平衡树(map),或离散化解决空间问题。同时,我们发现某个端点一定可以得到答案(感性理解)。所以,我们可以只存端点,用斜率乘距离来差分,即可统计答案。

代码

#include<map>
#include<cmath>
#include<cstdio>
#include<iostream>
using namespace std;
int n,a,b;
map<int,int>f;
long long ans;
void read(int &x){
	char c=getchar();
	for(;c<33;c=getchar());
	int f=1;
	if(c=='-'){
		f=-1;
		c=getchar();
	}
	for(x=0;(c>47)&&(c<58);x=x*10+c-48,c=getchar());
	x*=f;
}
int main(){
	freopen("teleport.in","r",stdin);
	freopen("teleport.out","w",stdout);
	read(n);
	for(int i=1;i<=n;i++){
		read(a);read(b);
		ans+=abs(a-b);
		if(abs(a)<=abs(a-b)){
			f[b]+=2;
			f[b-abs(a-b)+abs(a)]--;
			f[b+abs(a-b)-abs(a)]--;
		}
	}
	long long c=ans,s=0,l=-0x7fffffff;
	for(map<int,int>::iterator i=f.begin();i!=f.end();i++){
		int y=i->first,fx=i->second;
		c+=s*(y-l);
		l=y;
		s+=fx;
		ans=min(ans,c);
	}
	printf("%lld",ans);
	fclose(stdin);
	fclose(stdout);
}

猜你喜欢

转载自www.cnblogs.com/groundwater/p/12747675.html