2021 广东省程序设计竞赛(GDCPC)
回顾
本想有线下的比赛,未曾想因为疫情,最后还是线上举行了,临时借用一个会议室打的比赛,匆匆忙忙的过程中夹杂着大家对比赛的热情,等这么一个比赛也真的是等了很久。
比赛开始,首先看到的是A题,An Easy Problem,原想着噢,会不会这场比赛的签到就是A,看了下数据范围,诶,还是先通读一遍吧,看了看榜单,L题怎么过那么多人,输入描述:No input. 懂了,这才是签到题,马上抛开A题了,最后是qyg和xy过了这道题
qyg说看看这个博弈,然后xy说J好像是完全背包,qyg跟xy就讨论这个去了,我看博弈,刚开始也是理解错题意了(人为的想当然了)自己附加了一个条件,样例过了,结果wa了,结果我拉着qyg说,我给你分析一遍,你看一下过程有没有问题,qyg看了看题说:来吧。我开始分析,这样这样(省略 n 2 \frac{n}{2} 2n 个字) 我知道我错在哪了,改完,然后继续分析(省略 n 2 \frac{n}{2} 2n 个字)我感觉这样子应该没问题了,qyg:可以,我:那我交了?终于AC了,有点小激动。虽然这时候时间已经不多了,但是后面也想着,估计摸不到奖了,就不想了,就继续做吧。
(插播一个强烈谴责:临近期末,某王姓志愿者在复习计组,我在打比赛码题的过程中,他问我:老师说计组考什么?虽然当时我没有回他什么,但其实心里面已经骂了他千百遍了。 ^v^ [doge])
qyg和xy继续讨论着J题,J题交了几发,还是,qyg说,I题是个几何,是你的,我就去开I题了,大体的思想是对的,但是我理解成一个封闭体了,结果没过。xy说:J题我试试BFS吧,最后一发就过了。最后I题还是差点时间,总的来说,其实省赛的题目不算难,就自己太菜了,交了那么多发罚时,痛定思痛,思路来的太慢,刷的题太少,暑假当好好学习,希望接下来的比赛加加油吧!
比赛结束之后,打球的打球,吃饭的吃饭…
感想
其实这个比赛算是正式接触ACM的第一个比赛。说实话,真的挺激动的,还好最后是有个铜,虽然含金量不高,但至少在这方面能拿个奖了,还挺开心的,感谢队友 qyg 和 xysm 带我飞,感谢志愿者 (尤其是某王姓志愿者,这次比赛结束了,但这才是刚刚踏上acm的道路,希望我们能在这强者之林留下我们浅薄的脚印…,继续加油吧!
L. League of Legends(签到)
题意:有3个队伍进行战斗,1个队伍 VS 另一个队伍,获胜的和另外一个没参加的队伍进行战斗,直到出现某一支队伍失败次数达到2次。
求:进行战斗的场次 的期望值
一直1v1,直到出现某一支队伍失败次数达到2次,就结束。
那么失败次数达到2次的情况有:
(-1, -1, -2) ,(0, -1, -2) 有两种情况,所以答案就是:
3 × 0.5 + 4 × 0.5 = 3.5(直接PHP输出 [doge])
I. Industrial Nuclear Water (几何思想)
其实这道题呢就是一个简单的高中解析几何的思想。
有两个点
A
(
x
1
,
y
1
,
z
1
)
B
(
x
2
,
y
2
,
z
2
)
A(x_1, y_1, z_1) \;\;\;\;B(x_2, y_2, z_2)
A(x1,y1,z1)B(x2,y2,z2) ,
有6个障碍曲面:
1000
∣
x
∣
=
y
2
+
z
2
、
1000
∣
y
∣
=
x
2
+
y
2
、
1000
∣
z
∣
=
x
2
+
y
2
1000|x|=y^2+z^2、1000|y|=x^2+y^2、1000|z|=x^2+y^2
1000∣x∣=y2+z2、1000∣y∣=x2+y2、1000∣z∣=x2+y2
首先 不用管是什么曲面,知道就是一个面就完了
其次 为什么是6个障碍曲面呢?
1000
∣
x
∣
=
y
2
+
z
2
1000|x|=y^2+z^2
1000∣x∣=y2+z2 包括两个曲面
1000
x
=
y
2
+
z
2
1000x = y^2+z^2
1000x=y2+z2和
−
1000
x
=
y
2
+
z
2
-1000x = y^2+z^2
−1000x=y2+z2
依题意:如果这2个点在同一个区域就输出Yes,换言之,这2个点不被这6个曲面中任意1个曲面分割,则输出Yes
判断曲面是否分割2个点
如果:
1000
x
1
<
y
1
2
+
z
1
2
1000x_1<y_1^2+z_1^2
1000x1<y12+z12 且
1000
x
2
<
y
2
2
+
z
2
2
1000x_2<y_2^2+z_2^2
1000x2<y22+z22 则证明A、B点被曲面
1000
x
=
y
2
+
z
2
1000x=y^2+z^2
1000x=y2+z2 分割
如果:
1000
x
1
<
y
1
2
+
z
1
2
、
1000
x
2
<
y
2
2
+
z
2
2
1000x_1<y_1^2+z_1^2、1000x_2<y_2^2+z_2^2
1000x1<y12+z12、1000x2<y22+z22 或者
1000
x
1
>
y
1
2
+
z
1
2
、
1000
x
2
>
y
2
2
+
z
2
2
1000x_1>y_1^2+z_1^2、1000x_2>y_2^2+z_2^2
1000x1>y12+z12、1000x2>y22+z22 那么说明A,B两点在曲面
1000
x
=
y
2
+
z
2
1000x=y^2+z^2
1000x=y2+z2 的同一侧,即没有分割
综上:依次判断一下即可
#include<bits/stdc++.h>
#define int long long
using namespace std;
int check(int a, int b, int c) {
if (1000 * c < a * a + b * b) return true;
else return false;
}
int ok(int x1, int y1, int z1, int x2, int y2, int z2) {
int flag = 1;
if (check(x1, y1, z1) != check(x2, y2, z2)) flag = 0;
if (check(x1, y1, -z1) != check(x2, y2, -z2)) flag = 0;
if (check(x1, z1, y1) != check(x2, z2, y2)) flag = 0;
if (check(x1, z1, -y1) != check(x2, z2, -y2)) flag = 0;
if (check(z1, y1, x1) != check(z2, y2, x2)) flag = 0;
if (check(z1, y1, -x1) != check(z2, y2, -x2)) flag = 0;
return flag;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int _;
cin >> _;
while (_--) {
int x1, y1, z1, x2, y2, z2;
cin >> x1 >> y1 >> z1 >> x2 >> y2 >> z2;
if (ok(x1, y1, z1, x2, y2, z2)) cout << "Yes" << endl;
else cout << "No" << endl;
}
return 0;
}
G. Good Game, GG(一道不算博弈的博弈)
题意:A和B玩一场游戏,给定n个整数。
A有两种操作:
① 选一个奇数,将其分成两个整数
② 拿掉一个1
B只能有1种操作:选择一个偶数并且将这个偶数拆分成两个数
A为先手,判断谁是winner。
既然是博弈,那么就是找对应最优的操作
A最优的操作是:假设当前数是x,将奇数依次分解成x-2、2
如果分解出2的话,B如果再将2进行分解,那么只能分解成1、1,A的操作次数就多2次,A赚了。所以可以这么分解
所以A的操作次数是: a [ i ] / 2 + 1 a[i]/2 + 1 a[i]/2+1
B最优的操作是:假设当前数是x,将偶数进行分解,不要分解出奇数,只能是分解成两个偶数,
所以B的操作次数是: a [ i ] / 2 − 1 a[i]/2-1 a[i]/2−1
简单举例:
⚠ 记得开long long ⚠
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100010;
int a[N];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int _;
cin >> _;
while (_--) {
int n;
cin >> n;
int A = 0, B = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (a[i] % 2 == 1) A += a[i] / 2 + 1;
else B += a[i] / 2 - 1;
}
if (A <= B) cout << "Bob" << endl;
else cout << "Alice" << endl;
}
return 0;
}
J. Jerry(BFS)
题意:只能移动 完全平方数长度 的距离,问从0开始到x这个数,最少需要移动多少次。
先预处理100000以内的完全平方数,然后从0开始bfs,就可以找到最短距离了。
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int v[500], d[N];
bool st[N];
int cnt;
void init() {
for (int i = 1; i * i <= 1e5; i++) v[++cnt] = i * i;
}
void bfs() {
queue<int> q;
q.push(0);
st[0] = true;
while(!q.empty()) {
auto t = q.front();
q.pop();
for (int i = 1; i <= cnt; i++) {
if (v[i] + t <= 1e5 && !st[v[i] + t]) {
st[t + v[i]] = true;
d[t + v[i]] = d[t] + 1;
q.push(t + v[i]);
}
if (t - v[i] >= 0 && !st[t - v[i]]) {
st[t - v[i]] = true;
d[t - v[i]] = d[t] + 1;
q.push(t - v[i]);
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
init();
bfs();
int t;
cin >> t;
while (t--) {
int x;
cin >> x;
cout << d[x] << endl;
}
return 0;
}
A. An Easy Problem(优先队列)
求第k大的值,n,m的范围比较大,所以可以用优先队列存权值,每次要取出最大的一个,去k次,就是第k大,即答案。
#include<bits/stdc++.h>
#define x first
#define y second
#define int long long
using namespace std;
typedef pair<int, int> PII;
int n, m, k;
priority_queue<PII> q;
vector<int> a, b;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m, k;
cin >> n >> m >> k;
for (int i = 1; i <= n; i++) q.push({i * m, i});
k--;
while (k--) {
// cout << q.top().x << ' ' << q.top().y << endl;
auto t = q.top();
q.pop();//弹出k次
t.x -= t.y;
q.push(t);
}
int ans = q.top().x;
cout << ans << endl;
return 0;
}
D. Double(贪心)
题意:有n个人,排成一排,进行格斗游戏,依照规则,求能获得胜利的人有多少个。
题解:
先存一个最大战斗力的值maxx
依次枚举每一个人,当前这个人跟左方的人格斗;
如果a[i] > a[i-1]那么就能赢
如果a[i] == a[i -1]也认定能赢
赢了的能量值 × 2
一直到a[i] < a[i - 1] 或者 一直能赢到最左边
同理进行与右边的人进行格斗
如果期间出现能量值大于maxx,那么存在可以赢的可能。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 500010;
int a[N], b[N];
vector<int> ans;
signed main()
{
int n;
cin >> n;
int maxx = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
maxx = max(maxx, a[i]);
}
for (int i = 1; i <= n; i++) {
int t = a[i];
int l = i, r = i;
while (t < maxx) {
int f = 0;
if (t >= a[r + 1] && r + 1 <= n) {
t *= 2;
r++;
f = 1;
}
if (t >= a[l - 1] && l > 1) {
t *= 2;
l--;
f = 1;
}
if (!f) break;
}
if (t >= maxx) ans.push_back(i);
}
int len = ans.size();
cout << len << endl;
for (int i = 0; i < len; i++) cout << ans[i] << ' ';
cout << endl;
return 0;
}
F. Fake Math Problem(数学知识)
待续…