Codeforces Round #700 (Div. 2)A~D2解题报告
A Yet Another String Game
原题链接
http://codeforces.com/contest/1480/problem/A
解题思路
- Alice想让更小,先手
- Bob想让其更大,后手
- 解决方案当然是贪心,从第一个排到最后一个
- 如果不是选择当前未更改的第一个,那么被别人修改,那么就会往反方向走了
AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
char s[N];
int main()
{
int t; cin >> t;
while (t -- )
{
scanf("%s", s);
for (int i = 0; s[i]; i ++ )
{
if (i & 1) // bigger
{
if (s[i] != 'z')
putchar('z');
else
putchar('y');
}
else // smaller
{
if (s[i] != 'a')
putchar('a');
else
putchar('b');
}
}
puts("");
}
return 0;
}
B The Great Hero
原题链接
http://codeforces.com/contest/1480/problem/B
解题思路
就是一个英雄和一群小怪兽打,机制和洛克王国一样,问自己安排小怪兽出厂顺序,是否可以打死所有小怪兽(包括同归于尽)。
我们一直英雄的伤害为
A
A
A,血量为
B
B
B,
n
n
n个小怪兽的伤害为
a
i
a_i
ai,血量为
b
i
b_i
bi,可以计算出他可以承受的伤害为
c
i
=
⌈
b
i
/
A
⌉
c_i=\lceil b_i/A\rceil
ci=⌈bi/A⌉
英雄可以打过的充要条件是:
A
>
c
1
∗
a
1
+
.
.
.
+
c
i
∗
a
i
+
.
.
.
+
(
(
c
n
−
1
)
∗
a
n
)
A > c_1*a_1 +...+c_i * a_i +... +((c_n - 1)* a_n)
A>c1∗a1+...+ci∗ai+...+((cn−1)∗an) 注意这里是大于,因为保证最后一下可以打出去就行
⟹
\Longrightarrow
⟹
A
+
a
n
>
c
1
∗
a
1
+
.
.
.
+
c
i
∗
a
i
+
.
.
.
+
(
c
n
∗
a
n
)
A + a_n > c_1*a_1 +...+c_i * a_i +... +(c_n * a_n)
A+an>c1∗a1+...+ci∗ai+...+(cn∗an)
右边是常数,左面越大越好,也就是攻击力最大的放在最后即可
O
(
N
)
O(N)
O(N)
AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010;
int n;
LL A, B;
class Node
{
public:
int a, b;
}q[N];
int main()
{
int t; cin >> t;
while (t -- )
{
scanf("%lld%lld", &A, &B);
scanf("%d", &n);
int tmp;
int max_a = 0;
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &q[i].a);
max_a = max(max_a, q[i].a);
}
LL sum = 0;
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &tmp);
q[i].b = tmp / A + (tmp % A != 0); // 可以几回合
sum = sum + LL(q[i].a) * q[i].b;
}
if (B + max_a > sum)
puts("YES");
else
puts("NO");
}
return 0;
}
C Searching Local Minimum
原题链接
http://codeforces.com/contest/1480/problem/C
解题思路
二分题目
这个题目二分特性很难一眼看出,平时的二分基本是左侧为满足(不满足),右侧是不满足(满足),然后进行二分,有意思的地方就是在于满足的是什么特性!!
本题这个特性和平时遇到的特性不同, 特性是:区间[l,r]中至少含有一个local_min,至少含有一个是精髓! 因为对于本体而言局部最小值可能有很多,而且还很分散,我们需要找任意一个局部最小值,直接按照这个性质来找就可以二分。
需要注意的一点是,被我们舍弃的另一部分不是说不含有local_min,而是在我们未询问的前提下可能不含有local_min,而选中的那个区间一定含有。
AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010, INF = 0x3f3f3f3f;
int a[N];
void query(int idx)
{
if (a[idx] != -1) // 之前已经是有数字了
return;
printf("? %d\n", idx);
cout.flush();
scanf("%d", &a[idx]);
}
int n;
int main()
{
cin >> n;
memset(a, -1, sizeof a);
a[0] = INF, a[n + 1] = INF;
int l = 1, r = n, mid;
while (l < r)
{
mid = (l + r) / 2;
query(mid);
query(mid + 1);
if (a[mid] < a[mid + 1])
{
r = mid;
}
else
{
l = mid + 1;
}
}
printf("! %d\n", l);
return 0;
}
D1 Painting the Array I
原题链接
http://codeforces.com/contest/1480/problem/D1
解题思路
题目大概描述为,将原本的数组
a
1
,
a
2
,
.
.
.
,
a
n
a_1, a_2, ..., a_n
a1,a2,...,an通过染色分成两个子数组
a
1
(
0
)
,
a
2
(
0
)
,
.
.
.
,
a
n
1
(
0
)
a_1^{(0)}, a_2^{(0)}, ..., a_{n1}^{(0)}
a1(0),a2(0),...,an1(0)和
a
1
(
1
)
,
a
2
(
1
)
,
.
.
.
,
a
n
1
(
1
)
a_1^{(1)}, a_2^{(1)}, ..., a_{n1}^{(1)}
a1(1),a2(1),...,an1(1)使得合并数组毗邻相同的元素后,两个数组的长度和尽可能的大。
下面我们对原数组变形,形成以下形式
a
0
,
⋅
⋅
⋅
,
a
0
⏟
n
0
,
a
1
,
⋅
⋅
⋅
,
a
1
⏟
n
1
,
⋅
⋅
⋅
a
i
,
⋅
⋅
⋅
,
a
i
⏟
n
i
,
⋅
⋅
⋅
,
a
e
d
,
⋅
⋅
⋅
,
a
e
d
⏟
n
e
d
\underbrace{a_0, \cdot\cdot\cdot, a_0}_{n_0},\underbrace{a_1, \cdot\cdot\cdot, a_1}_{n_1},\cdot\cdot\cdot\underbrace{a_i, \cdot\cdot\cdot, a_i}_{n_i},\cdot\cdot\cdot,\underbrace{a_{ed}, \cdot\cdot\cdot, a_{ed}}_{n_{ed}}
n0
a0,⋅⋅⋅,a0,n1
a1,⋅⋅⋅,a1,⋅⋅⋅ni
ai,⋅⋅⋅,ai,⋅⋅⋅,ned
aed,⋅⋅⋅,aed。
\quad
其中
∀
i
,
a
i
≠
a
i
+
1
\forall i, a_i \not= a_{i+1}
∀i,ai=ai+1
假设我们当前正要处理
a
i
,
⋅
⋅
⋅
,
a
i
⏟
n
i
,
\underbrace{a_i, \cdot\cdot\cdot, a_i}_{n_i},
ni
ai,⋅⋅⋅,ai,部分时,分配的两个数组当前为
b
1
,
b
2
,
⋅
⋅
⋅
,
b
c
u
r
1
b_1,b_2,\cdot\cdot\cdot,b_{cur1}\quad
b1,b2,⋅⋅⋅,bcur1和
c
1
,
c
2
,
⋅
⋅
⋅
,
c
c
u
r
2
\quad c_1,c_2,\cdot\cdot\cdot,c_{cur2}
c1,c2,⋅⋅⋅,ccur2
下面我们对当前处理的情况进行讨论,
-
n
i
=
1
n_i=1
ni=1
b c u r 1 和 c c u r 2 至 少 存 在 一 个 不 等 于 a i b_{cur1}和c_{cur2}至少存在一个不等于a_{i} bcur1和ccur2至少存在一个不等于ai,那么直接放在不等于的后面, r e s + = 1 res += 1 res+=1 -
n
i
≥
2
n_i\geq 2
ni≥2
b c u r 1 和 c c u r 2 存 在 一 个 等 于 a i , 另 一 个 不 等 于 a i \quad b_{cur1}和c_{cur2}存在一个等于a_{i},另一个不等于a_{i} bcur1和ccur2存在一个等于ai,另一个不等于ai,那么直接放在不等于的后面(两边都放也行), r e s + = 1 \quad \quad res += 1 res+=1
b c u r 1 和 c c u r 2 都 不 等 于 a i \quad b_{cur1}和c_{cur2}都不等于a_{i} bcur1和ccur2都不等于ai,那么两边都放至少一个, r e s + = 2 \quad res += 2 res+=2
那么为什么这样做是对的?
补证明:
AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
const int INF = 0x3f3f3f3f;
int a[N];
int n;
int main()
{
cin >> n;
for (int i = 1; i <= n; i ++ )
scanf("%d", &a[i]);
int pre = 1, ed = 0, cnt;
a[0] = a[n + 1] = a[n + 2] = a[n + 3] = INF;
int res = 0;
int surp1 = -INF, surp2 = -INF;
int tmp1;
for (int i = 1; i <= n + 1; i ++ )
{
if (a[i] == a[i - 1])
{
ed = i;
}
else
{
cnt = ed - pre + 1;
if (cnt == 0)
{
pre = ed = i;
continue;
}
if (surp1 == a[i - 1] || surp2 == a[i - 1])
{
surp1 = surp2 = a[i - 1];
res += 1;
}
else if (cnt >= 2)
{
res += 2;
surp1 = surp2 = a[i - 1];
}
else if (cnt == 1) // 帮忙遮掩
{
tmp1 = a[i];
if (surp1 == a[i])
surp1 = a[i - 1];
else if (surp2 == a[i])
surp2 = a[i - 1];
else
surp1 = a[i - 1];
res += 1;
}
pre = ed = i;
}
}
cout << res << endl;
return 0;
}
D2 Painting the Array II
原题链接 待补
http://codeforces.com/contest/1480/problem/D2
解题思路
该题目也是贪心,
首先,原数组
a
0
,
⋅
⋅
⋅
,
a
0
⏟
n
0
,
a
1
,
⋅
⋅
⋅
,
a
1
⏟
n
1
,
⋅
⋅
⋅
a
i
,
⋅
⋅
⋅
,
a
i
⏟
n
i
,
⋅
⋅
⋅
,
a
e
d
,
⋅
⋅
⋅
,
a
e
d
⏟
n
e
d
\underbrace{a_0, \cdot\cdot\cdot, a_0}_{n_0},\underbrace{a_1, \cdot\cdot\cdot, a_1}_{n_1},\cdot\cdot\cdot\underbrace{a_i, \cdot\cdot\cdot, a_i}_{n_i},\cdot\cdot\cdot,\underbrace{a_{ed}, \cdot\cdot\cdot, a_{ed}}_{n_{ed}}
n0
a0,⋅⋅⋅,a0,n1
a1,⋅⋅⋅,a1,⋅⋅⋅ni
ai,⋅⋅⋅,ai,⋅⋅⋅,ned
aed,⋅⋅⋅,aed。
\quad
其中
∀
i
,
a
i
≠
a
i
+
1
\forall i, a_i \not= a_{i+1}
∀i,ai=ai+1。因为是要求最小,我们将数组改造为
a
0
,
a
1
,
⋅
⋅
⋅
,
a
i
,
⋅
⋅
⋅
,
a
e
d
a_0, a_1, \cdot \cdot \cdot,a_i, \cdot \cdot \cdot,a_{ed}
a0,a1,⋅⋅⋅,ai,⋅⋅⋅,aed
假设我们当前构造的两行
l
i
n
e
1
,
l
i
n
e
2
line1, line2
line1,line2是正确的,现在查看
a
[
i
]
a[i]
a[i]应该放在哪个队列之后。
- l i n e 1 _ b a c k ( ) = l i n e 2 _ b a c k ( ) line1\_back() =line2\_back() line1_back()=line2_back(),那么我们将 a [ i ] a[i] a[i]放在那里都是可以的。
-
l
i
n
e
1
_
b
a
c
k
(
)
≠
l
i
n
e
2
_
b
a
c
k
(
)
line1\_back() \not=line2\_back()
line1_back()=line2_back()时候,进行下列分析
倘若 l i n e 1 _ b a c k ( ) = a [ i ] line1\_back() = a[i] line1_back()=a[i],那么直接放在 l i n e 1 line1 line1即可,因为可以直接抵消掉
同理,倘若 l i n e 2 _ b a c k ( ) = a [ i ] line2\_back() = a[i] line2_back()=a[i],那么直接放在 l i n e 2 line2 line2即可。
最后 一种情况, l i n e 1 _ b a c k ( ) ≠ a [ i ] 而 且 l i n e 2 _ b a c k ( ) ≠ a [ i ] line1\_back() \not= a[i] 而且 line2\_back() \not= a[i] line1_back()=a[i]而且line2_back()=a[i],那么我们考虑 l i n e 1 line1 line1和 l i n e 2 line2 line2的末尾谁可以最近的匹配,放入较远的一方即可。(因为最近匹配优先,不可被中断)。
AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100010, INF = 0x3f3f3f3f;
int a[N], b[N];
int n;
vector<int> pos[N];
int cur[N];
int nxt(int x)
{
if (x == INF || cur[x] == pos[x].size()) // 已经到达了最后一个
return INF;
else
return pos[x][cur[x]];
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
int n2 = 1;
b[1] = a[1];
for (int i = 2; i <= n; i ++ )
if (a[i] != a[i - 1])
b[++ n2] = a[i];
n = n2;
memset(cur, 0, sizeof cur);
for (int i = 1; i <= n; i ++ )
{
// printf("%d ", b[i]);
pos[b[i]].push_back(i);
}
// puts("");
int res = n;
vector<int> v[2];
int tar = INF, cur_tar, tar_line = 1;
v[0].push_back(INF), v[1].push_back(INF);
for (int i = 1; i <= n; i ++ )
{
cur[b[i]] ++;
if (v[0].back() == v[1].back()) // 情况一,line1 == line2
{
if (v[0].back() == b[i])
res --;
v[0].push_back(b[i]);
}
else if (v[0].back() == b[i])
{
res --;
v[0].push_back(b[i]);
}
else if (v[1].back() == b[i])
{
res --;
v[1].push_back(b[i]);
}
else
{
static int nxt0, nxt1;
nxt0 = nxt(v[0].back());
nxt1 = nxt(v[1].back());
if (nxt0 >= nxt1)
{
v[0].push_back(b[i]);
}
else
{
v[1].push_back(b[i]);
}
}
}
cout << res << endl;
return 0;
}
/*
15
1 1 1 2 2 1 3 2 1 1 1 2 3 3 1
*/