U
S
A
C
O
2
USACO \ \ 2
USACO 2月比赛 铜组题解
比赛链接:http://usaco.org/
第一题:PALINDROME GAME
标签:思维、博弈
题意:给定一堆石子,有
S
S
S 个(
1
≤
S
≤
1
0
1
0
15
1≤S≤10^{10^{15}}
1≤S≤101015),
B
e
s
s
i
e
Bessie
Bessie和
E
l
s
i
e
Elsie
Elsie轮流拿回文正整数个石子,
B
e
s
s
i
e
Bessie
Bessie先手,当轮到一个人的时候 石子堆空了,该人输。两个人都执行最优策略,求谁会赢?
题解:
S
S
S数据很大,显然不能直接暴力模拟。我们可以找几个数据,推一推。
一位数的情况,
B
e
s
s
i
e
Bessie
Bessie直接拿走获胜。
两位数的情况,比如
10
10
10,
B
e
s
s
i
e
Bessie
Bessie至少得拿一个,不管拿多少个,剩下来都是一位数,
E
l
s
i
e
Elsie
Elsie赢。
11
、
12
、
13...19
11、12、13...19
11、12、13...19的情况,
B
e
s
s
i
e
Bessie
Bessie可以把个位直接拿走,留
10
10
10给
E
l
s
i
e
Elsie
Elsie,
B
e
s
s
i
e
Bessie
Bessie赢。
具体再看看
S
=
119
S=119
S=119之类的,发现只要
B
e
s
s
i
e
Bessie
Bessie把个位拿走,留
110
110
110给
E
l
s
i
e
Elsie
Elsie,也是必胜的,只要
E
l
s
i
e
Elsie
Elsie更改,
B
e
s
s
i
e
Bessie
Bessie把个位多余的拿走,最后就能留
10
10
10给
E
l
s
i
e
Elsie
Elsie。
推了下,我们发现个位为
0
0
0的为必败态,所以最终只要判
S
S
S个位是否为
0
0
0,就能知道是谁赢了。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
string s;
cin >> s;
int n = s.size();
if (s[n - 1] != '0') cout << "B" << endl;
else cout << "E" << endl;
}
return 0;
}
第二题:MILK EXCHANGE
标签:思维、倍增
题意:给定
N
N
N个数围成环。第
i
i
i个右边是第
i
+
1
i+1
i+1,左边是第
i
−
1
i-1
i−1。特殊的是第
1
1
1个数 左边是第
N
N
N个数,第
N
N
N个数右边是第
1
1
1个数。初始 第
i
i
i个数为
a
i
a_i
ai。给定一个字符串
s
1
,
s
2
,
.
.
.
s
n
s_1,s_2,...s_n
s1,s2,...sn,对于第
i
i
i个字符
s
i
s_i
si要么为
L
L
L,要么为
R
R
R。对于
s
i
=
L
s_i=L
si=L的情况下,
a
i
a_i
ai每分钟传递
1
1
1的值给左边;对于
s
i
=
R
s_i=R
si=R的情况下,
a
i
a_i
ai每分钟传递
1
1
1的值给右边。传递都是同时进行的。
传递的过程如果如果值超过了 初始的
a
i
a_i
ai 将会溢出,求经过
M
M
M分钟之后 这些所有数值之和为多少。
(
1
≤
N
≤
2
∗
1
0
5
,
1
≤
M
,
a
i
≤
1
0
9
1 ≤ N ≤ 2 * 10^5,1 ≤ M,a_i ≤ 10^9
1≤N≤2∗105,1≤M,ai≤109)
题解:数据很大,模拟每一分钟肯定不现实。我们先观察下一个例子
4 1
RRLL
1 1 1 1
初始
a
i
a_i
ai都是
1
1
1,求第
1
1
1分钟之后 数值之和。
第
1
1
1分钟的时候,
1
1
1往
2
2
2流,
2
2
2往
3
3
3流;
4
4
4往
3
3
3流,
3
3
3往
2
2
2流。我们能发现对于第
2
2
2个和第
3
3
3个来说是没有损失的。但是对于第
1
1
1个和第
4
4
4个来说损失了
1
1
1。
推一推能看出来,对于
R
R
R
R
L
L
L
L
L
L
RRRRLLLLLL
RRRRLLLLLL的,左边连续
R
R
R和右边连续
L
L
L都是流向中间
R
L
RL
RL的地方,然后分别溢出的。那溢出多少呢?是不是和时间
M
M
M相关。那我们其实就去把所有的
R
L
RL
RL找到,然后往左和往右,两边连续的
R
R
R和
L
L
L分别溢出
M
M
M减去,再求下和即可。注意特判一下全
L
L
L和全
R
R
R的情况。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5 + 10;
string s;
ll cntl = 0, cntr = 0;
ll a[N], n, m, ans = 0, sum = 0;
int main() {
cin >> n >> m;
cin >> s;
for (int i = 1; i <= n; i++) {
cin >> a[i];
sum += a[i];
if (s[i - 1] == 'L') cntl++;
else cntr++;
a[i + n] = a[i + 2*n] = a[i]; // 倍增
}
if (cntl == n || cntr == n) {
cout << sum << endl;
return 0;
}
s = "@" + s + s + s; // 环 倍增处理
for (int i = 1; i <= n; i++) {
if (s[i] == 'R' && s[i + 1] == 'L') {
ll l = i + n, r = i + 1 + n;
ll suml = 0, sumr = 0;
while (l > 1 && s[l - 1] == 'R') {
l--;
suml += a[l];
}
while (r < 3 * n && s[r + 1] == 'L') {
r++;
sumr += a[r];
}
// m轮之后 左边连续L剩余的 + 右边连续R剩余的
ll leave = max(suml - m, 0ll) + max(sumr - m, 0ll);
ans += a[i] + a[i + 1] + leave; // i和i+1位置牛奶量不变保留
}
}
cout << ans << endl;
return 0;
}
第三题:MAXIMIZING PRODUCTIVITY
标签:二分查找、思维
题意:给定
N
N
N个农场,第
i
i
i个农场在
c
i
c_i
ci的时候关闭。
B
e
s
s
i
e
Bessie
Bessie在时间
S
S
S的时候起床,计划在
t
i
+
S
t_i+S
ti+S的时间访问第
i
i
i个农场,必须在农场关闭之前去访问。进行
Q
Q
Q次查询,每次给出
S
S
S和
V
V
V,求是否能在时间
S
S
S的时候起床,并访问至少
V
V
V个农场。
(
1
≤
S
,
N
≤
1
0
6
,
1
≤
c
i
,
t
i
≤
1
0
6
,
1
≤
V
<
=
N
1≤S,N≤10^6,1≤c_i,t_i≤10^6,1≤V<=N
1≤S,N≤106,1≤ci,ti≤106,1≤V<=N)
二分查找解法 题解:因为查询次数比较多,直接暴力模拟 肯定会超时,我们需要做一些优化处理。题目中要求在农场关闭之前访问,所以我们可以把数组
c
c
c减去数组
t
t
t求个差值 再减一(因为题目要求关闭之前),求出第
i
i
i个如果要访问到,最迟的起床时间。然后对差值数组进行一下排序。
比如排序后的差值数组是:
−
3
、
2
、
4
、
7
、
10
、
15
-3、2、4、7、10、15
−3、2、4、7、10、15
假定当前查询的
S
=
4
,
V
=
3
S=4,V=3
S=4,V=3,那么可以通过二分找到第
3
3
3个位置,那么从这个位置往后到第
N
N
N个的位置都是可以访问到的,记
N
−
P
+
1
N-P+1
N−P+1个。(
P
P
P表示通过
l
o
w
e
r
_
b
o
u
n
d
lower\_bound
lower_bound函数找到的第一个大于等于
S
S
S的位置),然后我们与对应要求个数的
V
V
V对比判定一下输出即可。
二分查找解法 代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int c[N], t[N], v, s, n, q;
int main() {
cin >> n >> q;
for (int i = 1; i <= n; i++) cin >> c[i];
for (int i = 1; i <= n; i++) {
cin >> t[i];
c[i] = c[i] - t[i] - 1;
}
sort(c + 1, c + 1 + n);
while (q--) {
cin >> v >> s;
int p = lower_bound(c + 1, c + 1 + n, s) - c;
if (n - p + 1 >= v) cout << "YES" << endl;
else cout << "NO" << endl;
}
return 0;
}
思维解法 题解:和二分查找解法类似,因为题目中要求在农场关闭之前访问,所以我们可以把数组
c
c
c减去数组
t
t
t求个差值,再减一(因为题目要求关闭之前),然后对差值数组进行一下从大到小排序。
比如排序后的差值数组是:
15
、
10
、
7
、
4
、
2
、
−
3
15、10、7、4、2、-3
15、10、7、4、2、−3
假定当前查询的
S
=
4
,
V
=
3
S=4,V=3
S=4,V=3,判定 发现差值数组的第
3
3
3 个位置的值大于等于
S
S
S,那说明前
V
V
V个都能到达。如果同学们在做差值数组的时候没有减一,这边判大于就可以了。
思维解法 代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int c[N], t[N], v, s, n, q;
bool cmp(int x, int y) {
return x > y;
}
int main() {
cin >> n >> q;
for (int i = 1; i <= n; i++) cin >> c[i];
for (int i = 1; i <= n; i++) {
cin >> t[i];
c[i] = c[i] - t[i] - 1;
}
sort(c + 1, c + 1 + n, cmp);
while (q--) {
cin >> v >> s;
if (c[v] >= s) cout << "YES" << endl;
else cout << "NO" << endl;
}
return 0;
}