背景

靠队友带飞,我是演员(雾

题解

A - An Easy Problem

思路

类似有序数组合并的思路,可以用在这个题上面。

假设说,我们有$n$个数组,每个数组里面有$m$个元素,如$n=3, m=2$时:

然后,由于我们要求第$K$大的数,可以把每个数组最后面的元素都压到一个优先队列里,并以最后相乘得到的结果来规定优先队列的优先级。

其中,为了方便后续操作,我们可以规定这样的一个三元组$(n, nowm, g)$。

更详细的说:

  • $n$代表当前元素是第几个数组里的
  • $nowm$代表当前元素是第$n$个数组里的第几个元素
  • $g$是相乘的结果($|x| * |y|$)。

然后,压入优先队列,由上文,在最开始的时候,我们压入的是$(1, 2, 2), (2, 2, 4), (3, 2, 6)$。

我们找到这个优先队列里$g$最大的元素,即$(3, 2, 6)$,弹出。

然后,压入第$3$个数组里面,第$2-1$个元素,即压入三元组$(3, 1, 3)$。这样每次弹出的元素都是最大的。

类似的,重复$K$次,时间复杂度为$O(KlogN)$,可以通过本题。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <bits/stdc++.h>
#define int long long
#define maxn 1000005
using namespace std;
int n, m, K;
struct Node{
int n, nowm;
int g;
};
bool operator < (const Node &A, const Node &B){
return A.g < B.g;
}
priority_queue<Node> q1;
signed main(void)
{
scanf("%lld %lld %lld", &n, &m, &K);
for(int i = 1; i <= n; i++)
q1.push((Node){i, m, i * m});
int cnt = 0, now = -1;
while(cnt < K)
{
cnt++;
Node tmp = q1.top(); q1.pop();
now = tmp.g;
q1.push((Node){tmp.n, tmp.nowm - 1, tmp.n * (tmp.nowm - 1)});
}
printf("%lld\n", now);
return 0;
}

D - Double

思路

一个很关键的地方是,胜利后,每次这个元素都会翻倍

另一个关键的地方是,如果当前元素胜利后,翻倍后$\geq$最大值,则一定可以把剩下的元素吃掉。

因此,我们暴力枚举每一个点,并不断往左右拓展,由于翻倍的影响,最多只会拓展$30$次左右,就能大于等于最大值,即可以实现最后的胜利。

时间复杂度为$O(N)$。

本题有点类似花神游历各国(暴力开方就可以,因为一段数不会被开方很多次就会变成$0/1$)。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <bits/stdc++.h>
#define maxn 500005
using namespace std;
int n, maxx, a[maxn];
bool check(int pos){
int l = pos - 1, r = pos + 1, val = a[pos];
while(1)
{
if(val >= maxx) return true;
if(l && val >= a[l])
val *= 2, l--;
else if(r <= n && val >= a[r])
val *= 2, r++;
else
return false;
}
}
int cnt, ans[maxn];
int main(void)
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]), maxx = max(maxx, a[i]);
for(int i = 1; i <= n; i++)
if(check(i))
ans[++cnt] = i;
printf("%d\n", cnt);
for(int i = 1; i <= cnt; i++)
printf(i == cnt ? "%d\n" : "%d ", ans[i]);
return 0;
}

G-Good Game, GG

思路

考虑数组内全为$2$的情况为边界情况。

若数组全为$2$时,为$Alice$的回合,则$Bob$获胜。

否则,$Alice$必胜。

那么,讨论如何从最开始的状态达到这个边界情况。

假设,$Alice$在到达这种情况下能够执行$cnt1$步,$Bob$能执行$cnt2$步。

我们发现,$Alice$和$Bob$能够操作的数是互相独立的。

$Alice$为了使自己获胜,每次选择把一个奇数$k$分解为$k - 2$和$k$是最优的。

由此,某一奇数$k$能够为$Alice$提供$k/2 + 1$步操作($+1$是把最后的$1$给消去了,只剩下全为$2$的情况)。

而$Bob$为了使自己获胜,自然不会选择把一个偶数分解为两个奇数给对面,而是会选择分为两个偶数。

对于一个偶数$k$,在分解为边界情况前,可以为$Bob$提供$k/2-1$步操作。

最后,只要统计两人的总操作数,即$cnt1$和$cnt2$,就可以判断全$2$的局面是落在了谁的回合,即可以判断输赢。

记得开long long

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <bits/stdc++.h>
#define maxn 1000005
#define int long long
using namespace std;
int t, n, a[maxn];
signed main(void)
{
scanf("%lld", &t);
while(t--)
{
scanf("%lld", &n);
int cnt1 = 0, cnt2 = 0;
for(int i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
if(a[i] == 1) cnt1++;
else if(a[i] == 2) cnt2 += 0;
else if(a[i] & 1) cnt1 += a[i] / 2 + 1;
else cnt2 += a[i] / 2 - 1;
}
if(cnt1 > cnt2)
printf("Alice\n");
else
printf("Bob\n");
}
}

J - Jerry

思路

拿到这题时,发现这一题有$+ i i$的操作,也有$- i i$的操作,不太方便进行$dp$。

然后,写了个暴力打表程序,打了小的表后发现,貌似这些数都可以被$1,2,3$个完全平方数通过运算得到。

因此,提前计算出所有的完全平方数,大约有$300$个,可以写个$n^2+n^3$的枚举,计算最少需要多少个数才能凑出来。(结论的正确性我也不知道咋证明,但是在$1e5$内测试过没有不能表达出来的数字)。

赛后发现,实际上预处理所有的完全平方数,直接$bfs$就能通过该题了(

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <bits/stdc++.h>
#define maxn 200005
#define int long long
using namespace std;
int dp[maxn], bas[maxn], cnt;
void solve2(){
for(int i = 1; i <= cnt; i++)
for(int j = 1; j <= cnt; j++)
{
int val;

val = bas[i] + bas[j];
if(val >= 0 && val <= 100000)
dp[val] = min(dp[val], 2LL);

val = bas[i] - bas[j];
if(val >= 0 && val <= 100000)
dp[val] = min(dp[val], 2LL);
}
}
void solve3(){
for(int i = 1; i <= cnt; i++)
for(int j = 1; j <= cnt; j++)
for(int k = 1; k <= cnt; k++)
{
int val;

val = bas[i] + bas[j] + bas[k];
if(val >= 0 && val <= 100000)
dp[val] = min(dp[val], 3LL);

val = bas[i] + bas[j] - bas[k];
if(val >= 0 && val <= 100000)
dp[val] = min(dp[val], 3LL);

val = bas[i] - bas[j] + bas[k];
if(val >= 0 && val <= 100000)
dp[val] = min(dp[val], 3LL);

val = bas[i] - bas[j] - bas[k];
if(val >= 0 && val <= 100000)
dp[val] = min(dp[val], 3LL);
}
}
int q, n;
signed main(void)
{
memset(dp, 0x3f, sizeof(dp));
for(int i = 1; i * i <= 100000; i++)
{
bas[++cnt] = i * i;
dp[i * i] = 1;
}
solve2(); solve3();

scanf("%lld", &q);
while(q--)
{
scanf("%lld", &n);
printf("%lld\n", dp[n]);
}
return 0;
}

L - League of Legends

思路

一个期望题,签到,求最小的回合数。

设$(a, b, c)$分别为$a, b, c$三个人的落败数。

第一轮,任意一个人获胜,得到的结果都可以等效为$(0, 0\ | \ 1)$,落败的暂时出局。

然后,第二轮,剩下两个人战斗,得到结果可以等效为$(1, 0 \ | \ 1)$。

那么,其实最终的轮数只取决于第三轮,若落败过的选手再次落败,则结束回合,即变为$(2, 0 \ | \ 1)$。

否则,则继续第四局,且在第四局一定会有一个选手落败到两轮。

即第三轮过后变为$(1, 1 \ | \ 1)$,第四轮一定会结束。因此,期望应该是$3 0.5 + 4 0.5 = 3.5$。

I - Industrial Nuclear Water

思路

一个判断点位置的问题。

image-20210620224343372

$todo$

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <bits/stdc++.h>
#define int long long
#define maxn 1000005
using namespace std;
int inX(int x, int y, int z){
if (y * y + z * z <= 1000 * abs(x))
{
if (x > 0) return 1;
else return 2;
}
return 0;
}
int inY(int x, int y, int z){
if (z * z + x * x <= 1000 * abs(y))
{
if (y > 0) return 3;
else return 4;
}
return 0;
}
int inZ(int x, int y, int z){
if (y * y + x * x <= 1000 * abs(z))
{
if (z > 0) return 5;
else return 6;
}
return 0;
}

void judge(int x, int y, int z){
printf("%lld\n", inX(x, y, z));
printf("%lld\n", inY(x, y, z));
printf("%lld\n", inZ(x, y, z));
}

int t, x_1, y_1, z_1, x_2, y_2, z_2;
signed main(void)
{
//freopen("out.txt", "w", stdout);
scanf("%lld", &t);
while (t--)
{
scanf("%lld %lld %lld", &x_1, &y_1, &z_1);
scanf("%lld %lld %lld", &x_2, &y_2, &z_2);
// judge(x_1, y_1, z_1); putchar('\n');
// judge(x2, y2, z2); putchar('\n');
if (inX(x_1, y_1, z_1) == inX(x_2, y_2, z_2) &&
inY(x_1, y_1, z_1) == inY(x_2, y_2, z_2) &&
inZ(x_1, y_1, z_1) == inZ(x_2, y_2, z_2))
{
printf("Yes\n");
}
else
{
printf("No\n");
}
}
}