Social Distancing
题面
题目描述
由于高传染性的牛传染病 COWVID-19 的爆发,Farmer John 非常担忧他的奶牛们的健康。
为了限制疾病的传播,Farmer John 的 N 头奶牛
(
2
≤
N
≤
1
0
5
)
(2≤N≤10^5)
(2≤N≤105)决定践行“社交距离”,分散到农场的各处。农场的形状如一维数轴,上有 M 个互不相交的区间
(
1
≤
M
≤
1
0
5
)
(1≤M≤10^5)
(1≤M≤105),其中有可用来放牧的青草。奶牛们想要使她们位于不同的整数位置,每个位置上均有草,并且最大化 D 的值,其中 D 为最近的两头奶牛之间的距离。请帮助奶牛们求出 D 的最大可能值。
输入格式(文件名:socdist.in):
输入的第一行包含 N 和 M。以下 M 行每行用两个整数 a 和 b 描述一个区间,其中
0
≤
a
≤
b
≤
1
0
18
0≤a≤b≤10^{18}
0≤a≤b≤1018。没有两个区间有重合部分或在端点处相交。位于一个区间的端点上的奶牛视为在草上。
输出格式(文件名: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
b≤105。
测试点 4-10 没有额外限制。
算法分析
标准的二分答案。二分奶牛之间的距离D。
参考程序
Cereal
题面
题目描述
Farmer John 的奶牛们的早餐最爱当然是麦片了!事实上,奶牛们的胃口是如此之大,每头奶牛一顿饭可以吃掉整整一箱麦片。
最近农场收到了一份快递,内有
M
M
M 种不同种类的麦片(
1
≤
M
≤
1
0
5
1≤M≤10^5
1≤M≤105)。不幸的是,每种麦片只有一箱!
N
N
N 头奶牛(
1
≤
N
≤
1
0
5
1≤N≤10^5
1≤N≤105)中的每头都有她最爱的麦片和第二喜爱的麦片。给定一些可选的麦片,奶牛会执行如下的过程:
如果她最爱的麦片还在,取走并离开。
否则,如果她第二喜爱的麦片还在,取走并离开。
否则,她会失望地哞叫一声然后不带走一片麦片地离开。
奶牛们排队领取麦片。对于每一个
0
≤
i
≤
N
−
1
0≤i≤N−1
0≤i≤N−1,求如果 Farmer John 从队伍中移除前
i
i
i 头奶牛,有多少奶牛会取走一箱麦片。
输入格式(文件名:cereal.in):
输入的第一行包含两个空格分隔的整数
N
N
N 和
M
M
M。
对于每一个
1
≤
i
≤
N
1≤i≤N
1≤i≤N,第
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)
1≤fi,si≤M,且fi=si),为队伍中第
i
i
i 头奶牛最喜爱和第二喜爱的麦片。
输出格式(文件名:cereal.out):
对于每一个 0 ≤ i ≤ N − 1 0≤i≤N−1 0≤i≤N−1,输出一行,包含对于 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
N−1头牛,前
N
−
2
N-2
N−2头牛,…前
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
1≤N≤105)。粒子
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
xi≤xj 并且
y
i
≤
y
j
y_i≤y_j
yi≤yj 时会发生相互作用。在这些条件下,有可能这两个粒子中的一个会消失(另一个粒子不会发生任何变化)。在任意给定的时刻,至多只有一次相互作用会发生。
奶牛们想要知道在经过一些任意的相互作用之后剩余的哞粒子的最小数量。
输入格式(文件名: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;
}