USACO 2017 December GoldA Pie for a Pie 题解

Posted groundwater

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了USACO 2017 December GoldA Pie for a Pie 题解相关的知识,希望对你有一定的参考价值。

思路

首先注意到题目中的几个性质:

  1. 不会送出同一个 (pi) ,题目不允许,而且也不可能是最优方案。
  2. 如果送出某个 (pi) (除了第一个),那么刚刚收到的 (pi) 肯定是固定的某个 (pi)

那么基于此,设 (B_i) 为Bessie做的第 (i)(pi) 的答案、 (E_i) 为Elsie做的第 (i)(pi) 的答案,我们可以设出转移方程:

(f_i=egin{cases}1& ext{如果Elsie的评价为0}\min{{E_j}}& ext{如果Elsie对当前}pi ext{的评价}leq ext{对Elsie做的第}j ext{个}pi ext{的评价}end{cases})

(E_i=egin{cases}1& ext{如果Bessie的评价为0}\min{{F_j}}& ext{如果Bessie对当前}pi ext{的评价}leq ext{对Bessie做的第}j ext{个}pi ext{的评价}end{cases})

明显的(也就想了我半个小时),这是一个最短路的模型,所以我们把Bessie做的 (pi) 设成 (1)~(n) 号点,把Elsie做的 (pi) 设成 (n+1)~(n+n) 号点,然后建图,然后跑最短路即可。

实现

建图明显是 (n^2) 的(其实本来就是 (n^2) 的,但是数据……),考虑优化至(nlog{n}),有两种方法:

  1. 把Bessie做的 (pi) 按Bessie的评价排序,把Elsie做的 (pi) 按Elsie的评价排序。

    对于一个Bessie做的 (pi) ,用二分找到最小可以让Elsie送出哪个 (pi) ,然后一个一个建边直到不能建边为止。对于Elsie做的 (pi) 同理。

  2. 把所有的 (pi) copy一份放在另一个数组里,然后Bessie的 (pi) 分别按Bessie的评价和Elsie的评价排序。对于Elsie做的 (pi) 同理。

    对于一个Bessie做的 (pi) ,用另一个只增不减的指针找到最小可以让Elsie送出哪个 (pi) ,然后再用一个循环一个一个建边直到不能建边为止。对于Elsie做的 (pi) 同理。

注意:连边时要反着连,然后再构造一个源点连到每一个“出口”的点(即 (F_i)(E_i) 为1的点)。并不需要特殊处理答案,对于一个点只要有一个0就可以认为是“出口”(数据过水)。

然后建图就完成了,跑最短路即可。

Code

Warning:建边部分可能引起不适,请谨慎观看

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,w[200010],a[200010][2],cb[2][100010],ce[2][100010],last,h[200010],b[1500010][2],d[200010];
bool bz[200010];
void read(int &x){
	char c=getchar();
	for(;c<33;c=getchar());
	for(x=0;(47<c)&&(c<58);x=x*10+c-48,c=getchar());
}
bool comp1(int x,int y){
	return(a[x][0]<a[y][0]);
}
bool comp2(int x,int y){
	return(a[x][1]<a[y][1]);
}
void add(int x,int y){
	b[++last][0]=h[x];
	b[last][1]=y;
	h[x]=last;
}
void spfa(){
	int dh=0,dt=1;
	memset(w,127,sizeof(w));
	w[0]=0;
	while(dh<dt){
		for(int i=h[d[++dh]];i;i=b[i][0]){
			if(w[b[i][1]]>w[d[dh]]+1){
				w[b[i][1]]=w[d[dh]]+1;
				if(!bz[b[i][1]]){
					d[++dt]=b[i][1];
					bz[b[i][1]]=1;
				}
			}
		}
		bz[d[dh]]=0;
	}
}
int main(){
	freopen("piepie.in","r",stdin);
	freopen("piepie.out","w",stdout);
	read(n);read(m);
	for(int i=1;i<=n;i++){
		read(a[i][0]);read(a[i][1]);
		cb[0][i]=cb[1][i]=i;
	}
	for(int i=1;i<=n;i++){
		read(a[i+n][0]);read(a[i+n][1]);
		ce[0][i]=ce[1][i]=i+n;
	}
	sort(cb[0]+1,cb[0]+n+1,comp1);
	sort(cb[1]+1,cb[1]+n+1,comp2);
	sort(ce[0]+1,ce[0]+n+1,comp1);
	sort(ce[1]+1,ce[1]+n+1,comp2);
	for(int i=1,j=1;i<=n;i++){
		for(;a[cb[1][i]][1]>a[ce[1][j]][1]&&j<=n;j++);
		if(a[cb[1][i]][1]==0){
			add(0,cb[1][i]);
			continue;
		}
		for(int k=j;a[cb[1][i]][1]+m>=a[ce[1][k]][1]&&k<=n;add(ce[1][k],cb[1][i]),k++);
	}
	for(int i=1,j=1;i<=n;i++){
		for(;a[ce[0][i]][0]>a[cb[0][j]][0]&&j<=n;j++);
		if(a[ce[0][i]][0]==0){
			add(0,ce[0][i]);
			continue;
		}
		for(int k=j;a[ce[0][i]][0]+m>=a[cb[0][k]][0]&&k<=n;add(cb[0][k],ce[0][i]),k++);
	}
	spfa();
	for(int i=1;i<=n;i++){
		printf("%d
",w[i]==0x7f7f7f7f?-1:w[i]);
	}
	fclose(stdin);
	fclose(stdout);
	return(0);
}

以上是关于USACO 2017 December GoldA Pie for a Pie 题解的主要内容,如果未能解决你的问题,请参考以下文章

USACO 2017 December Contest Platinum T2: Push a Box

USACO 2017 December Contest Platinum T3: Greedy Gift Takers

USACO 2017 December Contest Gold T1: A Pie for a Pie

USACO 2016 December Contest Gold T1: Moocast

USACO 2007 December Contest, Silver Problem 2. Building Roads Kruskal最小生成树算法

USACO 2018 December Contest Platinum T2: Sort It Out