USACO2021 Dec.银组/USACO 2021 December Contest, Silver 题解

T1 Closest Cow Wins

题目

Farmer John owns a long farm along the highway that can be considered somewhat
like a one-dimensional number line. Along the farm, there are \(K\) grassy
patches (\(1 \leq K \leq 2\cdot 10^5\)); the \(i\)-th patch is located at position
\(p_i\) and has an associated tastiness value \(t_i\) (\(0\le t_i\le 10^9\)).
Farmer John's nemesis, Farmer Nhoj, has already situated his \(M\) cows
(\(1 \leq M \leq 2\cdot 10^5\)) at locations \(f_1 \ldots f_M\). All \(K+M\) of these
locations are distinct integers in the range \([0,10^9]\).

Farmer John needs to pick \(N\) (\(1\le N\le 2\cdot 10^5\)) locations (not
necessarily integers) for his cows to be located. These must be distinct from
those already occupied by the cows of Farmer Nhoj, but it is possible for
Farmer John to place his cows at the same locations as grassy patches.

Whichever farmer owns a cow closest to a grassy patch can claim ownership of
that patch. If there are two cows from rival farmers equally close to the
patch, then Farmer Nhoj claims the patch.

Given the locations of Farmer Nhoj's cows and the locations and tastiness values
of the grassy patches, determine the maximum total tastiness Farmer John's cows
can claim if optimally positioned.

Farmer John 沿着一条高速公路拥有一个很长的农场,可以被看作类似于一维数轴。沿着农场有 K 块草地(1≤K≤2⋅105);第 i 块草地位于位置 pi 并具有美味值 ti(0≤ti≤109)。Farmer John 的死对头 Farmer Nhoj 已经将他的 M 头奶牛(1≤M≤2⋅105)放在了位置 f1…fM 。所有这些 K+M 个位置均是 [0,109] 范围内的不同整数。

Farmer John 需要选择 N
(1≤N≤2⋅105

)个位置(不一定是整数)放置他的奶牛。这些位置必须与 Farmer Nhoj 的奶牛已经占用的位置不同,但是 Farmer John 可以将他的奶牛放在与草地相同的位置。

拥有最靠近某个草地的奶牛的农夫拥有这一草地。如果来自两方农夫的两头奶牛距这一草地相等,则 Farmer Nhoj 拥有该草地。

给定 Farmer Nhoj 的奶牛的位置以及草地的位置和美味值,求 Farmer John 的奶牛以最优方式放置时可以达到的最大总美味值。

思路

考虑在别人的两头奶牛之间放置多少头奶牛。

首先我们可以明确,在FN的任意两头奶牛之间,只能放0/1/2头奶牛。放0头相当于没有,放2头相当于拿走中间所以美味值,难点是放1头。

对于放一头的情况,这头奶牛必然可以且刚好拿到一半的位置,于是我们可以用双指针维护。当这一半位置的左端点移动时,右端点也跟着移动。

最后我们采用一个优先队列来维护。先把所有1头奶牛的情况丢到一个大根堆里,然后每次把1头奶牛pop出去后再把这个区间内2头减1头的数值塞到堆里即可。

总结

对于此类问题,我们可以分区间考虑。对于一些难处理的情况可以采用双指针解决。同时,对于答案可以通过优先队列来维护。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 200010
struct Node
{ 
	int x, y; 
	bool operator <(const Node &A) const { return x<A.x; }
}t; 
struct node { int x, y; }a[N]; 
int n, m, i, j, k; 
priority_queue<Node>q; 
int op, opx, s[N], f[N], ans, nw; 
int len, nx, nlen, l, r, id; 

bool cmp(node x, node y) { return x.x<y.x; }

int findx(int x) {
	if(a[n].x<x) return 1e17; 
	int l=1, r=n, mid; 
	while(l<r) {
		mid=l+r>>1; 
		if(a[mid].x<x) l=mid+1; 
		else r=mid; 
	}
	return l; 
}

int findy(int x) {
	if(a[1].x>x) return -1e17;  
	int l=1, r=n, mid; 
	while(l<r) {
		mid=l+r+1>>1; 
		if(a[mid].x<x) l=mid; 
		else r=mid-1; 
	}
	return l; 
}

signed main() {
	scanf("%lld%lld%lld", &n, &m, &k); 
	for(i=1; i<=n; ++i)
		scanf("%lld%lld", &a[i].x, &a[i].y), a[i].x*=2; 
	sort(a+1, a+n+1, cmp);
	for(i=1; i<=n; ++i) s[i]=s[i-1]+a[i].y; 
	for(i=1; i<=m; ++i) scanf("%lld", &f[i]), f[i]*=2; 
	sort(f+1, f+m+1); 
	f[0]=-1e17; f[m+1]=1e17; 
	for(id=0; id<=m; ++id) {
		l=findx(f[id]); r=findy(f[id+1]); 
		if(l>r) continue; 
		len=f[id+1]-f[id]; nw=0; 
		for(i=j=l; i<=r; ++i) {
			for(; j<=r; ++j)	
				if((a[j].x-a[i].x)*2>=len) break; 
			nw=max(nw, s[j-1]-s[i-1]); 
		}
		t.x=nw; t.y=s[r]-s[l-1]-t.x; q.push(t); 
	}
	for(i=1; i<=k; ++i) {
		t=q.top(); q.pop(); 
		ans+=t.x; t.x=t.y; t.y=0; 
		q.push(t); 
	}
	printf("%lld", ans); 
	return 0;
}

T2 Connecting Two Barns

题目

Farmer John's farm consists of a set of \(N\) fields \((1 \leq N \leq 10^5)\),
conveniently numbered \(1 \ldots N\). Between these fields are \(M\) bi-directed
paths \((0 \leq M \leq 10^5)\), each connecting a pair of fields.

The farm contains two barns, one in field 1 and the other in field \(N\). Farmer
John would like to ensure that there is a way to walk between the two barns
along some series of paths. He is willing to build up to two new paths to
accomplish this goal. Due to the way the fields are situated, the cost of
building a new path between fields \(i\) and \(j\) is \((i-j)^2\).

Please help Farmer John determine the minimum cost needed such that barns \(1\)
and \(N\) become reachable from each-other.

Farmer John 的农场由 \(N\) 块田地(\(1 \leq N \leq 10^5\))组成,编号为 \(1 \ldots N\)。在这些田地之间有 \(M\) 条双向道路(\(0 \leq M \leq 10^5\)),每条道路连接两块田地。

农场有两个牛棚,一个在田地 1 中,另一个在田地 \(N\) 中。Farmer John 希望确保有一种方式可以沿着一组道路在两个牛棚之间行走。 他愿意建造至多两条新道路来实现这一目标。由于田地的位置因素,在田地 \(i\)\(j\) 之间建造新道路的花费是 \((i-j)^2\)

请帮助 Farmer John 求出使得牛棚 \(1\)\(N\) 可以相互到达所需要的最小花费。

思路

由于只能连两条边,所以只有两种情况。

  1. 在1号田所在的块与 \(n\) 号田所在的块直接相连。

  2. 通过一个中转块,在1号田所在的块与 \(n\) 号田所在的块与这个中转块相连。

那么相连的最小代价是多少呢?

我们可以枚举期中一个块中的每一个点,然后在对应块中二分找出刚好大于和刚好小于的点,算出后去个min值即可。

要注意的是,我们要枚举中转块的点,因为这最多只有 \(n\) 个点,只会二分 \(n\) 次。枚举1号块或 \(n\) 号块的点容易超时。

可以用并查集和set做。

总结

对于题目一些给定且过小的性质,我们可以直接考虑情况。

在统计最小的过程中,可以二分上下界。

对于一些过大的枚举,可以反过来,就会变得很小。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 100010
int n, m, i, j, k; 
set<int>s[N]; 
set<int>::iterator it, jt, kt; 
int f[N], p[N], u, v, a, b, ans, t; 

int fa(int x) { return f[x]==x ? x : f[x]=fa(f[x]); }
int pingfang(int x) { return x*x; }

int len(int x, int y) {
	int ans=1e18; 
	for(it=s[x].begin(); it!=s[x].end(); ++it) {
		jt=s[y].lower_bound(*it); 
		if(jt!=s[y].end()) ans=min(ans, pingfang((*jt)-*it)); 
		if(jt!=s[y].begin()) ans=min(ans, pingfang((*(--jt))-*it)); 
	}
	return ans; 
}

signed main()
{
	scanf("%lld", &t);
	while(t--)
	{
		scanf("%lld%lld", &n, &m);
		for(i=1; i<=n; ++i) f[i]=i; 
		for(i=1; i<=n; ++i) s[i].clear(); 
		for(i=1; i<=m; ++i)
			scanf("%lld%lld", &u, &v), f[fa(u)]=fa(v); 
		for(i=1; i<=n; ++i) s[fa(i)].insert(i); 
		a=f[1]; b=f[n]; ans=len(a, b); 
		for(i=1; i<=n; ++i)
		{
			if(f[i]==a||f[i]==b||f[i]!=i) continue; 
			ans=min(ans, len(i, a)+len(i, b)); 
		}
		printf("%lld\n", ans); 
	}
	return 0;
}

T3 Convoluted Intervals

题目

The cows are hard at work trying to invent interesting new games to play. One of
their current endeavors involves a set of \(N\) intervals
(\(1\le N\le 2\cdot 10^5\)), where the \(i\)th interval starts at position \(a_i\) on
the number line and ends at position \(b_i \geq a_i\). Both \(a_i\) and \(b_i\) are
integers in the range \(0 \ldots M\), where \(1 \leq M \leq 5000\).

To play the game, Bessie chooses some interval (say, the \(i\)th interval) and her
cousin Elsie chooses some interval (say, the \(j\)th interval, possibly the same
as Bessie's interval). Given some value \(k\), they win if
\(a_i + a_j \leq k \leq b_i + b_j\).

For every value of \(k\) in the range \(0 \ldots 2M\), please count the number of
ordered pairs \((i,j)\) for which Bessie and Elsie can win the game.

奶牛们正在努力尝试发明有趣的新游戏来玩。他们目前的工作之一与一组 \(N\) 个区间(\(1\le N\le 2\cdot 10^5\))有关,其中第 \(i\) 个区间从数轴上的 \(a_i\) 位置开始,并在位置 \(b_i \geq a_i\) 结束。\(a_i\)\(b_i\) 均为 \(0 \ldots M\) 范围内的整数,其中 \(1 \leq M \leq 5000\)

这个游戏的玩法是,Bessie 选择某个区间(假设是第 \(i\) 个区间),而她的表妹 Elsie 选择某个区间(假设是第 \(j\) 个区间,可能与 Bessie 所选的的区间相同)。给定某个值 \(k\),如果 \(a_i + a_j \leq k \leq b_i + b_j\),则她们获胜。

对范围 \(0 \ldots 2M\) 内的每个值 \(k\),请计算使得 Bessie 和 Elsie 可以赢得游戏的有序对 \((i,j)\) 的数量。

思路

用差分思想考虑,所有 \(a_i+a_j\) 的地方+1,所以 \(b_i+b_j+1\) 的地方减1。

然而这是 \(O(n^2)\),但我们发现 \(a,b\) 的值域很小,所以我们可以用桶存起来,然后按值域遍历即可,就变成 \(O(m^2)\)

总结

对于一些过于重复的计算,如果值域很小,我们可以尝试用桶存起来,减去很多过于重复的计算。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 5010
int n, m, i, j, k; 
int a[N], b[N], x[N], y[N], c[N*2]; 

signed main()
{
	scanf("%lld%lld", &n, &m); 
	for(i=1; i<=n; ++i) scanf("%lld%lld", &j, &k), x[j]++, y[k]++; 
	for(i=0; i<=m; ++i)
		for(j=0; j<=m; ++j)
			c[i+j]+=x[i]*x[j], c[i+j+1]-=y[i]*y[j]; 
	for(i=k=0; i<=2*m; ++i) printf("%lld\n", k+=c[i]); 
	return 0;
}
posted @ 2021-12-21 18:38  zhangtingxi  阅读(621)  评论(0编辑  收藏  举报