比赛链接:http://usaco.org/
第一题:MAJORITY OPINION
标签:思维、模拟
题意:给定一个长度为
n
n
n的序列
a
a
a,操作:若区间
[
i
,
j
]
[i,j]
[i,j]内某个数字
k
k
k出现的次数 大于区间长度的一半,可以将区间内的所有数都换成这个数
k
k
k。经过多次操作之后,让区间
[
1
,
n
]
[1,n]
[1,n]内都为同一个数,输出所有可能的数(按照数字递增的顺序),若没有输出
−
1
-1
−1。(
1
<
=
n
<
=
1
0
5
,
1
<
=
a
i
<
=
n
1<=n<=10^5,1<=a_i<=n
1<=n<=105,1<=ai<=n)
题解:连续两个相同的数,不管是往前还是往后一个,都可以把加进来的数变成区间内的这个数。
比如
x
y
y
z
x\ y \ y \ z
x y y z可以往前把
x
x
x变成
y
y
y,往后把
z
z
z变成
y
y
y,那其实再往前或者再往后 可以把所有数都变成
y
y
y。
除此之外,还有
y
x
y
y \ x\ y
y x y的情况也是满足条件的,先把中间的这个
x
x
x变成
y
y
y,然后也可以把所有数都变成
y
y
y。
最终就变成了求第
i
i
i数和 第
i
−
1
i-1
i−1个数或者第
i
−
2
i-2
i−2个数 是否相同,相同的话,就可以去作为我们的答案,当然可能有重复的情况,所以要去重输出。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int t, n, a[N], res[N];
int main() {
cin >> t;
while (t--) {
cin >> n;
int c = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (i >= 2 && a[i] == a[i - 1]) res[++c] = a[i];
else if (i >= 3 && a[i] == a[i - 2]) res[++c] = a[i];
}
sort(res + 1, res + 1 + c);
c = unique(res + 1, res + 1 + c) - res - 1;
if (c == 0) cout << -1 << endl;
else {
for (int i = 1; i < c; i++) {
cout << res[i] << " ";
}
cout << res[c] << endl;
}
}
return 0;
}
第二题:CANNONBALL
标签:模拟
题意:给定一个从左往右
1
,
2
,
.
.
.
,
N
1,2,...,N
1,2,...,N标记的数轴,从其中某个位置
S
S
S开始往右跳,初始能量为
1
1
1。如果当前能量为
k
k
k,将会从当前位置往前跳
k
k
k的距离。
每个标记的位置,要么是跳板,要么是靶子,每个位置都有值
v
i
v_i
vi。
跳板:跳到跳板上,当这个位置的值是
v
i
v_i
vi,当前能量会增加
v
i
v_i
vi,并反转跳的方向。
靶子:跳到靶子上,如果当前能量大于等于 当前位置的值
v
i
v_i
vi,将会打破靶子,靶子被破坏掉后,将会一直维持破坏的状态,下次跳到这个位置无法被打破。
假设可以跳无限长的时间,或者离开数轴位置,能打破多少个靶子?(
1
<
=
N
<
=
1
0
5
1<=N<=10^5
1<=N<=105)
题解:维持当前朝向
d
i
r
dir
dir,当前能量
e
n
e
r
g
y
energy
energy,模拟跳的过程,当跳的过程中
s
<
1
s<1
s<1或者
s
>
n
s>n
s>n结束。每次按照规则进行模拟跳,跳板的时候增加能量值,反转跳的方向,打靶的时候标记一下靶子是否被破坏过。
如果在同一个能量值跳到 某个位置 两次,那么可以得到以后的循环不会有新的靶子被破坏,再之后就是重复新的一轮了。小估估一下时间复杂度,让它尽量跳,超出轮数默认不会再产生新的打破靶子数。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, s, q[N], v[N], ans = 0;
bool vis[N];
int main() {
cin >> n >> s;
for (int i = 1; i <= n; i++) {
cin >> q[i] >> v[i];
}
int energy = 1, dir = 1; // 能量、朝向
for (int i = 1; i <= 1000 * N; i++) {
if (q[s] == 0) { // 跳板
energy += v[s];
dir *= -1;
}
else { // 靶子
if (!vis[s] && energy >= v[s]) {
vis[s] = 1;
ans++;
}
}
s += dir * energy;
if (s < 1 || s > n) break;
}
cout << ans << endl;
return 0;
}
第三题:BALANCING BACTERIA
标签:思维、差分
题意:给定
n
n
n个数,
a
1
,
a
2
,
a
3
.
.
.
a
n
a_1,a_2,a_3...a_n
a1,a2,a3...an,每次操作 可以选择数字
L
(
1
<
=
L
<
=
n
)
L(1<=L<=n)
L(1<=L<=n),并选择增加或者减少。
比如增加的情况,从第
n
n
n个数开始,第
n
n
n个数增加
L
L
L,第
n
−
1
n-1
n−1个数增加
L
−
1
L-1
L−1,第
n
−
2
n-2
n−2个数增加
L
−
2
L-2
L−2…,依次类推,直到增加值为
0
0
0,再往前就不增加(即第
1
1
1个到第
n
−
L
n-L
n−L个数不增加值)
求将所有数变成
0
0
0的最少操作次数。(
1
<
=
n
<
=
2
∗
1
0
5
,
1
0
−
15
<
=
a
i
<
=
1
0
15
1<=n<=2*10^5,10^{-15}<=a_i<=10^{15}
1<=n<=2∗105,10−15<=ai<=1015)
举个例子:比如有两个数
−
1
3
-1\ \ \ 3
−1 3
可以先从选择数字
L
=
1
L=1
L=1并进行减少,操作
5
5
5次,把序列变成:
−
1
−
2
-1\ \ \ \ -2
−1 −2
然后选择数字
L
=
2
L=2
L=2,并进行增加,操作
1
1
1次,把序列变成:
0
0
0\ \ \ 0
0 0。总共
6
6
6次操作。
题解:以样例
2
2
2为例,有
5
5
5个数:
1
3
−
2
−
7
5
1\ \ \ 3 \ \ -2 \ \ -7 \ \ \ 5
1 3 −2 −7 5
我们维护一个差分数组
b
i
b_i
bi为
a
i
−
a
i
−
1
a_i-a_{i-1}
ai−ai−1:
1
2
−
5
−
5
12
1\ \ \ 2 \ \ -5 \ \ -5 \ \ \ 12
1 2 −5 −5 12
这个差分数组表示原
a
a
a数组中相邻两个数的差值,我们额外对
i
=
1
i=1
i=1的时候求
b
1
=
a
1
−
a
0
b_1=a_1-a_0
b1=a1−a0,
a
0
=
0
a_0=0
a0=0。
再对差分数组
b
i
b_i
bi做差分,
c
i
=
b
i
−
b
i
−
1
c_i=b_i-b_{i-1}
ci=bi−bi−1:
1
1
−
7
0
17
1 \ \ 1 \ \ -7 \ \ 0 \ \ 17
1 1 −7 0 17
原
a
i
a_i
ai:
1
3
−
2
−
7
5
1\ \ \ 3 \ \ -2 \ \ -7 \ \ \ 5
1 3 −2 −7 5
第
1
1
1轮
a
i
a_i
ai:
−
1
−
2
−
3
−
4
−
5
=
>
0
1
−
5
−
11
0
-1\ -2\ -3\ -4 \ -5=> 0\ \ \ 1 \ \ -5 \ \ -11 \ \ \ 0
−1 −2 −3 −4 −5=>0 1 −5 −11 0 (操作次数
+
1
+1
+1)
第
2
2
2轮
a
i
a_i
ai:
0
−
1
−
2
−
3
−
4
=
>
0
0
−
7
−
14
−
4
0\ -1\ -2\ -3 \ -4=> 0\ \ \ 0 \ \ -7 \ \ -14 \ \ \ -4
0 −1 −2 −3 −4=>0 0 −7 −14 −4 (操作次数
+
1
+1
+1)
第
3
3
3轮
a
i
a_i
ai:
0
0
1
2
3
=
>
0
0
0
0
17
0\ 0\ 1\ 2 \ 3=> 0\ \ \ 0 \ \ 0 \ \ 0 \ \ \ 17
0 0 1 2 3=>0 0 0 0 17 (操作次数
+
7
+7
+7)
第
4
4
4轮
a
i
a_i
ai:
0
0
0
0
0
=
>
0
0
0
0
17
0\ 0\ 0\ 0 \ 0=> 0\ \ \ 0 \ \ 0 \ \ 0 \ \ \ 17
0 0 0 0 0=>0 0 0 0 17 (操作次数
+
0
+0
+0)
第
5
5
5轮
a
i
a_i
ai:
0
0
0
0
−
1
=
>
0
0
0
0
0
0\ 0\ 0\ 0 \ -1=> 0\ \ \ 0 \ \ 0 \ \ 0 \ \ \ 0
0 0 0 0 −1=>0 0 0 0 0 (操作次数
+
17
+17
+17)
我们能够发现每次归
0
0
0的操作次数就是对应
c
i
c_i
ci的绝对值。其实就是对
b
i
b_i
bi进行差分,得到每个对应的后缀需要修改多少。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
typedef long long ll;
ll a[N], b[N], n, ans = 0;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
b[i] = a[i] - a[i - 1]; // 差分
}
for (int i = 1; i <= n; i++) {
ans += abs(b[i] - b[i - 1]);
}
cout << ans << endl;
return 0;
}