【题解】USACO 2020 OPEN Sliver(银组)

Social Distancing

题面

题目描述
由于高传染性的牛传染病 COWVID-19 的爆发,Farmer John 非常担忧他的奶牛们的健康。
为了限制疾病的传播,Farmer John 的 N 头奶牛 ( 2 ≤ N ≤ 1 0 5 ) (2≤N≤10^5) 2N105决定践行“社交距离”,分散到农场的各处。农场的形状如一维数轴,上有 M 个互不相交的区间 ( 1 ≤ M ≤ 1 0 5 ) (1≤M≤10^5) 1M105,其中有可用来放牧的青草。奶牛们想要使她们位于不同的整数位置,每个位置上均有草,并且最大化 D 的值,其中 D 为最近的两头奶牛之间的距离。请帮助奶牛们求出 D 的最大可能值。
输入格式(文件名:socdist.in):
输入的第一行包含 N 和 M。以下 M 行每行用两个整数 a 和 b 描述一个区间,其中 0 ≤ a ≤ b ≤ 1 0 18 0≤a≤b≤10^{18} 0ab1018。没有两个区间有重合部分或在端点处相交。位于一个区间的端点上的奶牛视为在草上。
输出格式(文件名:socdist.out):
输出 D 的最大可能值,使得所有奶牛之间的距离均不小于 D 单位。保证存在 D>0 的解。
输入样例:

5 3
0 2
4 7
9 9

输出样例:

2

样例解释
取到 D=2 的一种方式是令奶牛们处在位置 0、2、4、6 和 9。
测试点性质:
测试点 2-3 满足 b ≤ 1 0 5 b≤10^5 b105
测试点 4-10 没有额外限制。

算法分析

标准的二分答案。二分奶牛之间的距离D。

参考程序


Cereal

题面

题目描述

Farmer John 的奶牛们的早餐最爱当然是麦片了!事实上,奶牛们的胃口是如此之大,每头奶牛一顿饭可以吃掉整整一箱麦片。
最近农场收到了一份快递,内有 M M M 种不同种类的麦片( 1 ≤ M ≤ 1 0 5 1≤M≤10^5 1M105)。不幸的是,每种麦片只有一箱! N N N 头奶牛( 1 ≤ N ≤ 1 0 5 1≤N≤10^5 1N105)中的每头都有她最爱的麦片和第二喜爱的麦片。给定一些可选的麦片,奶牛会执行如下的过程:

如果她最爱的麦片还在,取走并离开。
否则,如果她第二喜爱的麦片还在,取走并离开。
否则,她会失望地哞叫一声然后不带走一片麦片地离开。
奶牛们排队领取麦片。对于每一个 0 ≤ i ≤ N − 1 0≤i≤N−1 0iN1,求如果 Farmer John 从队伍中移除前 i i i 头奶牛,有多少奶牛会取走一箱麦片。

输入格式(文件名:cereal.in)

输入的第一行包含两个空格分隔的整数 N N N M M M
对于每一个 1 ≤ i ≤ N 1≤i≤N 1iN,第 i i i 行包含两个空格分隔的整数 f i f_i fi s i s_i si 1 ≤ f i , s i ≤ M , 且 f i ≠ s i ) 1≤f_i,s_i≤M,且 f_i≠s_i) 1fi,siMfi=si,为队伍中第 i i i 头奶牛最喜爱和第二喜爱的麦片。

输出格式(文件名:cereal.out):

对于每一个 0 ≤ i ≤ N − 1 0≤i≤N−1 0iN1,输出一行,包含对于 i i i 的答案。

输入样例:

4 2
1 2
1 2
1 2
1 2

输出样例:

2
2
2
1

如果至少两头奶牛留下,那么恰有两头奶牛取走了一箱麦片。

测试点性质:

测试点 2-3 满足 N,M≤1000。

测试点 4-10 没有额外限制。

算法分析

模拟。
如果依次模拟删除前 0 0 0头牛,前 1 1 1头牛,…
如果删除第 i i i头牛,那么第 i i i头牛选的麦片有可能被第 j j j头牛选,第 j j j头牛选的麦片有可能被第 k k k头牛选…
当删除第 i i i头牛,麦片的选择会产生连锁反应,有可能所有的奶牛的选择都会被打乱。
在这里我们逆序考虑。
逆序考虑,先考虑删除前 N − 1 N-1 N1头牛,前 N − 2 N-2 N2头牛,…前 0 0 0头牛
考虑第 i i i头牛,第 i + 1 i+1 i+1~ N N N头牛已经选择。如果第 i i i头牛第一喜欢的还在,那么直接选择,如果被第 j ( j > i ) j(j>i) j(j>i)头牛选了,那么抢过来( i i i排在前面有优先权),如果第 j j j头牛被抢的是第一喜欢的就考虑第二喜欢的,否则就不选。
每头牛只会有三种选择,不会存在连锁反应。总的时间复杂度为 O ( N 2 ) O(N_2) O(N2)

参考程序
#include<bits/stdc++.h>
using namespace std;
const int N=100100;
int n,m,f[N],s[N];
int cnt,ans[N],vis[N];  //标记
void dfs(int k,int x)
{
    if(!vis[x]) 
    {
        vis[x]=k;
        cnt++;
        return;
    }
    else if(vis[x]>k)    //被后面的奶牛选了 
    {
        int t=vis[x];
        vis[x]=k;       //把后面奶牛选的抢了(前面的奶牛先选) 
        if(f[t]==x) dfs(t,s[t]);    //后面的奶牛选第二喜欢的,否则就没得 
    }
} 
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&f[i],&s[i]);
    for(int i=n;i>=1;i--)
    {
        dfs(i,f[i]);        //选最喜欢的 
        ans[i-1]=cnt;   
    } 
    for(int i=0;i<n;i++)
        printf("%d\n",ans[i]); 
    return 0;   
} 

The Moo Particle

题目描述
在 COWVID-19 爆发期间 Farmer John 的奶牛们为了安全进行了隔离,她们想到了一种全新的方式缓解无聊:研究高等物理!事实上,她们甚至成功发现了一种新的亚原子粒子,她们将其命名为“哞粒子”。
奶牛们正在进行一项有关 N N N 个哞粒子的实验( 1 ≤ N ≤ 1 0 5 1≤N≤10^5 1N105)。粒子 i i i 的“自旋”可以用范围在 − 1 0 9 −10^9 109 1 0 9 10^9 109 之间的两个整数 x i x_i xi y i y_i yi 来描述。有时两个哞粒子会发生相互作用。自旋为 ( x i , y i x_i,y_i xi,yi) 和 ( x j , y j x_j,y_j xj,yj) 的两个粒子之间仅当 x i ≤ x j x_i≤x_j xixj 并且 y i ≤ y j y_i≤y_j yiyj 时会发生相互作用。在这些条件下,有可能这两个粒子中的一个会消失(另一个粒子不会发生任何变化)。在任意给定的时刻,至多只有一次相互作用会发生。
奶牛们想要知道在经过一些任意的相互作用之后剩余的哞粒子的最小数量。
输入格式(文件名:moop.in):
输入的第一行包含一个整数 N,为哞粒子的初始数量。以下 N 行每行包含两个空格分隔的整数,为一个粒子的自旋。每个粒子的自旋各不相同。
输出格式(文件名:moop.out):
输出一个整数,为经过一些任意的相互作用之后剩余的哞粒子的最小数量。
输入样例:

4
1 0
0 1
-1 0
0 -1

输出样例:

1

样例解释
一个可能的相互作用顺序:
粒子 1 和 4 相互作用,粒子 1 消失。
粒子 2 和 4 相互作用,粒子 4 消失。
粒子 2 和 3 相互作用,粒子 3 消失。
仅留下粒子 2。
输入样例:

3
0 0
1 1
-1 3

输出样例:

2

样例解释
粒子 3 不能与任何其他两个粒子相互作用,所以它必然会留下。粒子 1 和 2 中必然留下至少一个。
测试点性质:
测试点 3-6 满足 N≤1000。
测试点 7-12 没有额外限制。

算法分析

解法一:并查集
如果用边将能够互相作用的粒子连在一起,那么在同一个连通块中的粒子,可以相互作用,直到只剩下一个粒子,最后的答案就是连通块的数量。可以使用并查集实现,一个连通块就是一个集合,但是时间复杂度为 O ( N 2 ) O(N^2) O(N2),只能得一半的分数。

解法二:
将x从小到大排序,y从小到大排序。
考虑y坐标。
有右侧几个点,前面三个点组成一个集合,考虑第四个点i?
在这里插入图片描述
只要在i之前找到y值比yi小的点或者在i之后找到比yi大的点都能够相互作用
如何在最快的时间找到在i之前比yi小,在i之后比yi大的点?
前缀最小y值,后缀最大y值

参考程序
#include<bits/stdc++.h>
using namespace std;
const int N=100100;
int n;
struct node
{
	int x,y;
}a[N];
int miny[N],maxy[N];
bool comp(node u,node v)
{
	if(u.x==v.x)	return u.y<v.y?1:0;
	else return u.x<v.x?1:0;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d%d",&a[i].x,&a[i].y);
	sort(a+1,a+n+1,comp);	//x,y从小到大排序 
	miny[1]=a[1].y;
	for(int i=2;i<=n;i++)	//前缀最小纵坐标y
		miny[i]=min(miny[i-1],a[i].y);
	maxy[n]=a[n].y;
	for(int i=n-1;i>=1;i--)	//后缀最大纵坐标 
		maxy[i]=max(maxy[i+1],a[i].y); 
	int ans=1;
	for(int i=1;i<=n;i++)
		if(miny[i]>maxy[i+1])	ans++;	
	//当miny[i]<=maxy[i+1]时,当前点i肯定可以和最小的y或者最大的y组成一组(x已经从小到大排序了) ,否则单独成一个集合
	cout<<ans<<endl; 
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
USACO2022金组是国际在线判题系统USACO的最级别,题目难度较,在该比赛中取得好成绩是一项巨大的成就。以下是对该比赛的一些题目解析。 第一题:“交通计划” 题目要求:给定一个n个节点的有向图,每条边有一个长度,希望添加最少的边使得所有节点连通,求最小生成树的权值和。 解析:该题可以使用Kruskal算法求解,将每条边按权值从小到大排序,再依次加入,判断加入的边是否会形成环,若形成则不加入,直到所有节点连通为止。此时Kruskal算法得到的最小生成树的权值和即为所求。 第二题:“点火计划” 题目要求:给定一个n个节点的有向图,每条边有一个权值和一个点火时长,每个节点有一个点火启动时刻和时刻结束时刻,希望从其中选出一些边点火,使得所有节点都可从点火的边出发到达,且所选点火边的总点火时长最小。 解析:该题可以使用最小费用最大流算法求解。将每条边看做一个容量为1,费用为点火时长的边,源点向节点的点火边容量为1,费用为0的边,节点的点火边向汇点的容量为1,费用为0的边,对这个网络进行最小费用最大流即可得到所选边的总点火时长最小。 第三题:“美味佳肴” 题目要求:给定n个菜品,每个菜品有它的权值和两个类别,希望选出k个菜品,使得选出的菜品数量在每个类别中都不超过$\frac{k}{3}$个,且所选菜品的权值和最大。 解析:该题可以使用动态规划求解。设$f[i][j][k]$表示前i个菜品中,选择j个一类菜品,选择k个二类菜品的最大权值和,状态转移方程为$f[i][j][k]=max(f[i-1][j][k],f[i-1][j-1][k]+a[i],f[i-1][j][k-1]+b[i])$,其中a[i]为i号菜品的权值,若为一类则为该权值,否则为0,b[i]为i号菜品的权值,若为二类则为该权值,否则为0。最终答案为$f[n][$k/3$][$k/3$]。 以上是对USACO2022金组的部分题目的解析,USACO比赛是全球范围内的计算机竞赛,竞争非常激烈,能够在该比赛中脱颖而出是一项非常棒的成就。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值