前言
不开ll见祖宗!我愿称之为因为ll打烂了的月赛。
D是签到题这里不再写了。
Problem A(未A)
思路
数学题,非常妙,赛场上只想出来了一半。
一开始火速搓了一个二分思想wa掉了,做完别的再回来看就想到了m的作用:由于电子秤且知道真假的质量,那么第一堆放一个,第二堆放两个,直到第m堆放m个,我们就可以知道称上是否有、有几个假,由此,每次可以测出m+1堆。
剩下是我没想到的:如果n>m+1了,我们可以把m+1堆捆成一坨,造m+1坨,若确定了一坨,则在本坨里量一次即可,依次类推,我们可知答案为log(m+1) n。
Problem B
简单写个函数,没什么好说的。
AC代码
#include <bits/stdc++.h>
using namespace std;
int f(int a, int b, int c, int d, int x) {
int ans = a * x * x * x + b * x * x + c * x + d;
return ans;
}
int main() {
int n;
scanf("%d", &n);
int a, b, c, d, q, w, e, r, x1, x2;
while (n--) {
cin >> a >> b >> c >> d >> q >> w >> e >> r >> x1 >> x2;
cout << max(f(a, b, c, d, x1), f(q, w, e, r, x2)) << '\n';
}
return 0;
}
Problem E
思路
不懂的话就模拟一下八进制或者模拟一遍样例,苯人就是爱模拟。
在数字b中,每位数字都是num取模序列a中相应位,然后把整除的数给到下一位作为进位,和十进制是一样的模式,只是数变了可能就会有点晕。
另:前几天看了大数加法、乘法、阶乘和,都是一个理念,思路非常像,想通一个就都通了。
回顾
做这题的时候排名好像已经不太好看了,所以有点急,第一次wa了之后开始检查,发现了一个没开ll的量,但我觉得不是因为这里错了,就又改了两遍输出格式,废了一个小数+2wa,这个时候排名可以说是非常难看了,真的很难绷,然后直接开摆改回去加了ll,没想到他过了,绿a出现的那一刻彻底绷不住了,真想给自己俩大比兜,坐赛场上缓了得有十分钟才继续做题。
真的因为ll摔了大跟头,以后不可以再因为ll错。
AC代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int main() {
ll x;
int n;
int arr[10000], b[10000] = {0};
cin >> n >> x;
ll t = x;
for (int j = 0; j < n; ++j) { //看提目要求,前面数都是0了也要输出
cin >> arr[j];
b[j] = t % arr[j]; //每一位都是基数的模
t /= arr[j]; //进位
}
cout << b[0];
for (int j = 1; j < n; ++j) {
cout << " " << b[j];
}
cout << '\n';
return 0;
}
Problem J
思路
这场比赛感谢这个博弈捞了我一下,E题罚时的影响太大了。对这题我的评价是,奇偶性真的可以玩的很花。
赛时思路:样例很重要,样例给我了一点灵感。因为我个人比较抵触跟矩阵沾边的东西,所以其实一开始我的思路是把矩形都变成一行长条(样例让我灵机一动),那么2、3步一轮如何获胜,这样就很容易想到奇偶性,然后再找问题,条和矩形的区别是什么。我的思维会优先广搜,找到最简的可能思路再深想,所以其实我的思路会有点跳跃,我尽量整理成顺一点的(但是在赛场上直接想到顺的思路其实很难,这要求你把每一个点都理解的很透并且看到就想到,所以我更倾向于多看方法,跳跃思维,避免一条路走到黑发现是南墙。但是这样也有弊端,就是一些正解就是麻烦的题我会去寻找别的做法,试试这试试那,浪费时间且不一定能搞出来)。
复盘:(我目前遇到的)博弈的特点就是放弃枚举的思路,一旦开始想枚举就完了,越想越乱,从找规律的思维入手,氪金每次可走1或2步,我每次只能走1步,那我们玩一局游戏走的步数可能是2或3, 因为我们都绝对聪明且无法掌控对方走向,所以要找的就是只有一种结局的情况。
我先手,如果边长和是偶数,那么氪金只需要每次走一步,无论怎么走我必败。
如果边长和是奇数,若氪金还是每次走一步,那氪金必败,他该如何破解这个情况呢,显然就是在某一轮中走两步,让剩下的边长和回归偶数情况则可保证必胜。于是我唯一胜利的可能就是不让氪金有走两步的机会,氪金要想走两步只能是斜对角走,什么情况他不能再斜对角走了呢:有一边已经到墙了。思路到这里已经差不多成型了:边长和是奇数的时候,当本来就无法走斜线时我必赢;当有两列或两行,由于我先手,在第一次走的时候走一列/一行让他无法继续走斜线时我必赢。其他情况则他必赢。
AC代码
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int m, n;
cin >> m >> n;
bool flag = 0;
if ((m + n) % 2 == 0) {
flag = 0;
} else {
if (m == 0 || n == 0 || m == 1 || n == 1) {
flag = 1;
}
}
if (flag == 1)
cout << "win\n";
else
cout << "loss\n";
}
return 0;
}
Problem M(未A)
思路
dfs&&bfs,补题A了,但是因为交错了题wa了一下午,笑死,这就是笨比。
dfs注意点:是否需要回溯。
油田和马踏飞燕其实是两种题型,油田只需要找出没被找过的@就可以,只需标记不用回溯,其实相当于只查了一遍全图。而马踏飞燕是求组合,需要遍历很多遍同一个部分,所以在每一part的遍历前标记,遍历后回溯。
#include <bits/stdc++.h> //dfs
using namespace std;
#define ll long long
ll K = 998244353;
ll C = 499122177; //先算出2的逆元,省去了快速幂
ll n, m;
ll cnt = 13;
void dfs(ll cur, ll step) {
if (step > 12) {
return;
}
if (cur == m) {
cnt = min(cnt, step); //类比1015二叉树
return;
}
for (int i = 1; i <= 3; ++i) {
ll b;
switch (i) {
case 1:
b = cur * 3 % K;
break;
case 2:
b = cur * C % K;
break;
case 3:
b = (cur - 123456) % K;
break;
}
dfs(b, step + 1); //这里不用标记,与全排列不同,而更像马踏飞燕
} //因为每一步都可以走一样的,同一个数的step不同也会影响答案
}
int main() {
int T;
cin >> T;
while (T--) {
cnt = 13; //初始化
cin >> n >> m;
dfs(n, 0);
if (cnt == 13)
cout << "-1\n";
else
cout << cnt << '\n';
}
return 0;
}
bfs注意点:初始化初始化初始化,清空队列清空队列清空队列。标记注意点见下。
#include <bits/stdc++.h> //bfs
using namespace std;
#define ll long long
const ll K = 998244353;
const ll C = 499122177;
ll m;
struct stu {
ll num;
int step;
};
queue<stu>q;
map<ll, bool>vis; //标记
void bfs() {
while (!q.empty()) {
stu cur = q.front();
q.pop();
//vis[cur.num] = 1;出队时标记没什么大用,见下。
if (cur.num == m) {
cout << cur.step << '\n';
return;
}
for (int i = 1; i <= 3; ++i) {
stu b;
b.step = cur.step + 1;
switch (i) {
case 1:
b.num = cur.num * 3 % K;
break;
case 2:
b.num = cur.num * C % K;
break;
case 3:
b.num = (cur.num - 123456 + K) % K;
break;
}
if (b.step <= 12 && vis[b.num] != 1)
q.push(b);
vis[b.num] = 1; //要在入队的时候就标记,或者选到他了就标记。
//与dfs不同,bfs由于广度,能保证不走回头路,cur状态的step永远是已判断序列中最大的
//不需考虑dfs上述step不同结果不同的情况。
//若仅在出队时判断,那在其入队后出队前,可能会被重复入队,无法达到判断目的。
}
}
cout << "-1\n";
return;
}
int main() {
int t;
cin >> t;
while (t--) {
vis.clear(); //清空标记
while (!q.empty()) { //清空队列
q.pop();
}
stu a;
a.step = 0;
cin >> a.num >> m;
q.push(a);
bfs();
}
return 0;
}
总结
绷住心态,记得开ll,人不能总在同一个地方摔跟头,记住。