USACO 12月比赛 铜组题解
基本上都是思维题,同学们开动自己的小脑瓜嗷~
比赛链接:http://usaco.org/
第一题:CANDY CANE FEAST
标签:思维、枚举、模拟
题意:给定
n
n
n头牛的初始高度和
m
m
m个糖果棒的初始高度,每个糖果棒都要
n
n
n头牛轮流吃一波过去,牛吃了多长的糖果棒,就会长多高。但是每头牛只能吃到小于等于它高度并且有的糖果棒。求最后这
m
m
m个糖果棒都吃完,这
n
n
n头牛的高度。(
1
<
=
n
,
m
<
=
2
∗
1
0
5
1<=n,m<=2*10^5
1<=n,m<=2∗105,牛和糖果棒高度范围:
[
1
,
1
0
9
]
[1,10^9]
[1,109])
先通过样例,再深入理解一下题意。
给定
3
3
3 头牛,每头牛的初始高度分别为
3
、
2
、
5
3、2、5
3、2、5;给定
2
2
2个糖果棒,每个糖果棒的初始高度分别为
6
、
1
6、1
6、1。
第一个糖果棒情况:一开始糖果棒高度
[
1
,
6
]
[1,6]
[1,6],第一头牛(高度为
3
3
3)吃掉
[
1
,
3
]
[1,3]
[1,3]部分,还剩
[
4
,
6
]
[4,6]
[4,6]部分;第二头牛高度(高度为
2
2
2)太低,吃不到;第三头牛(高度为
5
5
5)能吃到糖果棒的
[
4
,
5
]
[4,5]
[4,5]部分。
牛的高度变成
6
、
2
、
7
6、2、7
6、2、7。
第二个糖果棒情况:第一头牛(高度为
6
6
6),能把第二个糖果棒
[
1
,
1
]
[1,1]
[1,1]全部吃掉,增长高度
1
1
1。
牛的高度变成
7
、
2
、
7
7、2、7
7、2、7
题解:把题意理解之后,其实就是一个模拟题。可以维护一下糖果棒目前可以吃的高度区间
[
l
,
r
]
[l,r]
[l,r]。当牛的高度完全大于等于
r
r
r(即能够把糖果棒全部吃掉),那就把目前糖果棒能吃的部分全部加到牛的高度上;如果牛的高度是在
[
l
,
r
]
[l,r]
[l,r]中间部分的,那么牛能吃的部分就是牛的高度
a
[
j
]
a[j]
a[j]减去
l
l
l。注意在枚举的过程中记得维护一下
l
l
l。(
r
r
r一开始就定好了,没必要去更改)
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, m, l, r, k, a[200005];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= m; i++) {
cin >> r;
l = 1;
for (int j = 1; j <= n; j++) {
if (a[j] >= r) {
a[j] += r - l + 1;
break;
}
else if (a[j] >= l) {
k = a[j];
a[j] += a[j] - l + 1;
l = k + 1;
}
}
}
for (int i = 1; i <= n; i++)
cout << a[i] << endl;
return 0;
}
第二题:COWNTACT TRACING 2
标签:思维、枚举、模拟
题意:给定长度为
n
n
n的字符串(只包含
01
01
01),求用初始最少的
1
1
1,每天每一个
1
1
1都会把相邻的两个位置都变成
1
1
1,经过若干天后形成给定的这个字符串。(
1
<
=
n
<
=
3
∗
1
0
5
1<=n<=3*10^5
1<=n<=3∗105)
我们通过样例解释一下,比如要形成长度为
5
5
5的
11111
11111
11111字符串。
可以一开始在第三个位置放
00100
00100
00100,第一天:
01110
01110
01110,第二天:
11111
11111
11111,所以初始只要一个
1
1
1。
题解:比较容易想到我们需要把连续的
1
1
1的个数拆出来。比如
00011101101111001
00011101101111001
00011101101111001=>
3
2
4
1
3 \ 2 \ 4 \ 1
3 2 4 1
首先我们去思考每一团连续的
1
1
1要形成的最多天数是多少?(为什么是最多天数呢,因为天数越多,初始的
1
1
1就越少)不好想的话,那我们举几个例子:
1
1
1:
0
0
0天
11
11
11:
0
0
0天(这里可能有些人会有疑惑,其实中间部分的和左右两边的情况 我们到时候要分类讨论的,这边姑且先考虑中间部分的)
111
111
111:
1
1
1天
1111
1111
1111:
1
1
1天
11111
11111
11111:
2
2
2天
111111
111111
111111:
2
2
2天
.
.
.
.
.
.
......
......
往下不断推下去,能推出最多天数其实是不是就是1的个数减去
1
1
1,再除以
2
2
2。(其实就是从最中间往外扩散的过程)
这是每一团连续的
1
1
1要形成的最多天数,那么我们要让每一团的符合,得去拿每一团的最多天数中的最小值(
m
i
n
D
a
y
minDay
minDay),让每一团连续的
1
1
1都满足天数要求。
因为每一团要让天数往下降,很简单,只要让初始的
1
1
1多一点就好了。
在求出
m
i
n
D
a
y
minDay
minDay的基础上,我们接下来就是去求每一团在这样一个天数下,需要初始的
1
1
1的个数。
举个例子:比如
m
i
n
D
a
y
=
2
minDay=2
minDay=2,某一团连续的
1
1
1是
1111111
1111111
1111111,那么我们初始的
1
1
1应该是
0010100
0010100
0010100
这道题还有个需要注意的点是:最左(
1
1
1)和最右(
n
n
n)连续的
1
1
1具有特殊性,最大天数能到达这一团的
1
1
1的个数减一。因为如果最左设置
1
1
1,往左扩散是会碰墙,过不去的;最右同理。
代码:
#include <bits/stdc++.h>
using namespace std;
vector<int> v;
int main() {
int n;
string s;
cin >> n >> s;
int pre = 0, suf = n - 1, cnt = 0, minDay = n + 1;
while (s[pre] == '1') pre++;
while (suf > pre && s[suf] == '1') suf--;
if (pre != 0) {
v.push_back(pre);
minDay = min(minDay, pre - 1);
}
if (suf != n - 1) {
v.push_back(n - 1 - suf);
minDay = min(minDay, n - 2 - suf);
}
for (int i = pre; i <= suf; i++) {
if (s[i] == '1') cnt++;
else if (s[i] == '0') {
if (cnt > 0) {
v.push_back(cnt);
minDay = min(minDay, (cnt - 1) / 2);
}
cnt = 0;
}
}
if (cnt > 0) {
v.push_back(cnt);
minDay = min(minDay, (cnt - 1) / 2);
}
int ans = 0;
for (int i = 0; i < v.size(); i++) {
int k = v[i] / (minDay * 2 + 1);
if (v[i] % (minDay * 2 + 1)) k++;
ans += k;
}
cout << ans;
return 0;
}
第三题:FARMER JOHN ACTUALLY FARMS
标签:思维、数学
题意:给定
n
n
n个数字初始值
h
i
h_i
hi,第
i
i
i个数字每天会增加
a
i
a_i
ai,再给出包含
0
0
0到
n
−
1
n-1
n−1之间所有数字的数组
t
i
t_i
ti,求满足 对于第
i
i
i 个数字,正好有
t
i
t_i
ti 个比它更大的数字 的最少天数。
题解:题目要求对于对于第
i
i
i个数字,正好有
t
i
t_i
ti 个比它更大的数字,那么我们肯定得从最小(
t
i
=
0
t_i=0
ti=0)的数开始,然后第二小的数(
t
i
=
1
t_i=1
ti=1)…,在这个过程中,不断地让相邻的两个数看看能不能在初始值为
h
i
h_i
hi,每天增量为
a
i
a_i
ai的情况下满足后一个数大于前一个数,在这个过程中不断地去求这个天数的最大值。
所以一开始我们得去做个映射:第
t
i
t_i
ti大的数是输入的第
i
i
i个数。
然后在第
1
1
1小的数、第
2
2
2小的数、第
3
3
3小的数… 的过程中,看看两个数是否不符合当前数大于前一个数不符合,且在当前数增量大于前一个数增量的情况下,去求需要增量增加的天数,因为需要满足所有的,所以维护这边天数的最大值。
需要注意的是,想让结果是大于的情况,可以给被除数(数字初始值只差
+
1
+1
+1),再进行处理。
最后再判定一下,修改完之后的序列是否满足题目中要求的顺序,不满足输出
−
1
-1
−1。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5 + 10;
ll h[N], a[N], t[N], f[N];
int main() {
ll T, n;
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i <= n; i++) cin >> h[i];
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) {
cin >> t[i];
f[t[i]] = i;
}
ll ans = 0;
for (int i = 1; i < n; i++) {
if (a[f[i-1]] > a[f[i]] && h[f[i-1]] < h[f[i]]) {
ll k = (h[f[i]] - h[f[i-1]] + 1) / (a[f[i - 1]] - a[f[i]]);
if ((h[f[i]] - h[f[i-1]] + 1) % (a[f[i - 1]] - a[f[i]])) k++;
ans = max(ans, k);
}
}
for (int i = 1; i <= n; i++) h[i] += ans * a[i];
bool flag = 1;
for (int i = 1; i < n; i++) {
if (h[f[i-1]] <= h[f[i]]) flag = 0;
}
if (flag) cout << ans << endl;
else cout << -1 << endl;
}
return 0;
}