个人题解
C 上进的凡凡
解题思路
双指针,相当于每次用指针 left 和 right 划分出单调不减的一个子数组,让右指针勇往直前。如果 right 碰到不符合条件的数了,就开始计算前面这一段里面的子数组总数。
计算方法就是等差数列求和。如,前面划出的一段里有 3 3 3 个数,那么它们能够组成的子数组数量为 1 + 2 + 3 1 + 2 + 3 1+2+3 个。
感谢有巨巨带我不然真的心态崩了 不知道哪个是签到题就在那边乱做
参考代码
#include<bits/stdc++.h>
#define VI vector<int>
typedef long long LL;
typedef double db;
const int inf = 0x3f3f3f3f;
const LL INF = 1e18;
const int maxn = 1e5 + 10;
using namespace std;
int readint() {int x; scanf("%d", &x); return x;}
int a[maxn];
int main() {
int n = readint();
a[0] = -1;
a[n + 1] = -1;
for(int i = 1; i <= n; i++) a[i] = readint();
LL ans = 0;
int left = 1, right = 2;
while (right <= n + 1) {
if (a[right] < a[right - 1]) { //如果现在开始不能构成不减序列
int k = right - left;
ans += 1LL * k * (k + 1) / 2;
left = right; //左指针移过来
right++; //右指针继续探索
} else {
right++;
}
}
printf("%lld", ans);
return 0;
}
D Seek the Joker I
解题思路
联想到bash博弈。bash博弈的规则是,有 n n n 个东西,两个人每次最多能抽 k k k 个,抽到最后一个东西的玩家获胜。其规律是, n m o d ( k + 1 ) = 0 n \mod (k + 1) = 0 nmod(k+1)=0,后手胜,否则先手胜。
本题是修改版,规则是抽到最后一个东西的玩家输。即:抽到第 n − 1 n - 1 n−1 个东西的玩家胜,因此直接套用 bash 博弈的结论。
博弈太难了555
参考代码
#include<bits/stdc++.h>
#define VI vector<int>
typedef long long LL;
typedef double db;
const int inf = 0x3f3f3f3f;
const LL INF = 1e18;
const int maxn = 1e5 + 10;
using namespace std;
int readint() {int x; scanf("%d", &x); return x;}
int main() {
int t = readint();
while (t--) {
int n = readint(), k = readint();
if ((n - 1) % (k + 1) == 0) printf("ma la se mi no.1!");
else printf("yo xi no forever!");
printf("\n");
}
return 0;
}
F 成绩查询
解题思路
查询,注意数据存取的方式。详细见代码。
这才是我错过的签到题吧
参考代码
#include<bits/stdc++.h>
#define VI vector<int>
typedef long long LL;
typedef double db;
const int inf = 0x3f3f3f3f;
const LL INF = 1e18;
const int maxn = 1e5 + 10;
using namespace std;
int readint() {int x; scanf("%d", &x); return x;}
struct student{
char name[30];
int grade;
int sex;
int id;
}s[maxn];
bool cmp(int a, int b) { //输出按照字典序排序
string s1 = s[a].name;
string s2 = s[b].name;
return s1 < s2;
}
int main() {
int n = readint();
unordered_map<string, int> name;
vector<int> g[110];
for(int i = 0; i < n; i++) {
scanf("%s %d%d%d", s[i].name, &s[i].grade, &s[i].sex, &s[i].id);
name[s[i].name] = i;
g[s[i].grade].push_back(i);
}
int m = readint();
while (m--) {
int t = readint();
if (t == 1) {
char now[30];
scanf("%s", now);
int pos = name[now];
printf("%d %d %d\n", s[pos].grade, s[pos].id, s[pos].sex);
} else {
int gg = readint();
if (!g[gg].empty()) {
sort(g[gg].begin(), g[gg].end(), cmp);
for(int i = 0; i < g[gg].size(); i++) {
printf("%s\n", s[g[gg][i]].name);
}
}
}
}
return 0;
}
H 数羊
解题思路
先打个 n n n 在 100 100 100 以内的表,发现 m m m 三个值的情况下 n ( n ≥ 2 ) n(n \ge 2) n(n≥2) 是有规律的, n = 1 n = 1 n=1 时特判一下即可。
参考代码
#include<bits/stdc++.h>
#define VI vector<int>
typedef long long LL;
typedef double db;
const int inf = 0x3f3f3f3f;
const LL INF = 1e18;
const int maxn = 1e9 + 10;
using namespace std;
const int mod = 998244353;
int readint() {int x; scanf("%d", &x); return x;}
LL quick(LL a, LL b) { //快速幂
LL res = 1;
while (b) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res % mod;
}
int a[100][100];
int reg(int n, int m) { //此处是打表的代码
if (n == 1 && m == 0) return 2;
if (n == 0 && m >= 0) return 1;
if (m == 0 && n >= 2) return n + 2;
if (a[n][m] != 0) return a[n][m];
return a[n][m] = reg(reg(n - 1, m), m - 1);
}
int main() {
int t = readint();
while (t--) {
LL n;
scanf("%lld", &n);
int m = readint();
if (n == 1) printf("2");
else {
if (m == 0) printf("%lld", (n + 2) % mod);
else if (m == 1) printf("%lld", (n << 1) % mod);
else printf("%lld", (quick(2LL, n)) % mod);
}
printf("\n");
}
return 0;
}
I 买花
解题思路
等比数列求和,枚举 k k k 即可。
谁能想到这题 WA 了,输出有毒啊,考我视力orz
参考代码
#include<bits/stdc++.h>
#define VI vector<int>
typedef long long LL;
typedef double db;
const int inf = 0x3f3f3f3f;
const LL INF = 1e18;
const int maxn = 1e5 + 10;
using namespace std;
int readint() {int x; scanf("%d", &x); return x;}
LL quick(LL a, LL b) {
LL res = 1;
while (b) {
if (b & 1) res = res * a;
a = a * a;
b >>= 1;
}
return res;
}
int main() {
int t = readint();
LL n;
while (t--) {
bool flag = false;
cin >> n;
for(int k = 2; k <= 15; k++) {
if (n % (quick(2, 1LL * k) - 1) == 0) { //如果求出来首项为整数
flag = true;
break;
}
}
printf("%s\n", flag ? "YE5" : "N0");
}
return 0;
}
J 这是一题简单的模拟
解题思路
按照题目要求去判断路径是否合法,之后更新答案。
看 N N N 不大,使用邻接矩阵存边,方便询问两个点之间是否有连边。判断的时候统计访问过的城市个数。
参考代码
#include<bits/stdc++.h>
#define VI vector<int>
typedef long long LL;
typedef double db;
const int inf = 0x3f3f3f3f;
const LL INF = 1e18;
const int maxn = 310;
using namespace std;
int readint() {int x; scanf("%d", &x); return x;}
int M[maxn][maxn];
int f[maxn][maxn];
bool vis[maxn];
void init() {
memset(M, 0, sizeof(M));
memset(vis, 0, sizeof(vis));
}
int main() {
int n = readint(), m = readint();
int a, b, ff;
init();
while (m--) {
scanf("%d%d%d", &a, &b, &ff);
M[a][b] = M[b][a] = 1;
f[a][b] = f[b][a] = ff;
}
int k = readint();
int ans = inf;
while (k--) {
memset(vis, 0, sizeof(vis));
int pre = 0, sum = 0, cnt = 0; //pre保存上一个顶点
bool flag = true;
int t = readint();
t++; //为后面方便判断,相当于加上最后一个顶点0
while (t--) {
int now;
if (t == 0) now = 0;
else now = readint();
if (M[now][pre] == 0) {
flag = false;
// break; //此处注意不能break,否则数据没有完全读进来
} else {
if (!vis[now] && now) {
vis[now] = 1;
cnt++;
}
sum += f[now][pre];
pre = now;
}
}
if (flag && cnt == n) //路径合法
ans = min(sum, ans);
}
printf("%d", ans == inf ? -1 : ans);
return 0;
}
K 黑洞密码
解题思路
直接模拟,其中题意理解了半天,加减字母的规则是:如果碰到 z z z,那么它再加一,得到的是 B B B; Z Z Z 下一位是 b b b。
没想到很简便的写法,就硬写的。
参考代码
#include<bits/stdc++.h>
#define VI vector<int>
typedef long long LL;
typedef double db;
const int inf = 0x3f3f3f3f;
const LL INF = 1e18;
const int maxn = 310;
using namespace std;
int readint() {int x; scanf("%d", &x); return x;}
char change(int k, char c) { //字母c往后面推k位
while (k) { //k变成0的时候退出
if (c >= 'a' && c <= 'z') {
if (c + k > 'z') {
k -= ('z' - c) + 1;
c = 'B';
} else {
c += k;
return c;
}
}
if (c >= 'A' && c <= 'Z') {
if (c + k > 'Z') {
k -= ('Z' - c) + 1;
c = 'b';
} else {
c += k;
return c;
}
}
}
return c;
}
int main() {
string s, s1, s2, ans;
cin >> s;
for(int i = 0; i < s.size(); i++) {
if (isdigit(s[i])) s2.push_back(s[i]);
else s1.push_back(s[i]);
}
ans = "";
for(int i = 0; 4 * i + 3 < s1.size(); i++) {
string tmp;
for(int k = 4 * i; k <= 4 * i + 3; k++) {
char now = change(s2[k] - '0', s1[k]);
tmp.push_back(now);
}
for(int k = tmp.size() - 1; k >= 0; k--)
ans.push_back(tmp[k]);
}
cout << ans;
return 0;
}
L 建立火车站
解题思路
二分枚举站台之间的最大距离。
参考代码
#include<bits/stdc++.h>
#define VI vector<int>
typedef long long LL;
typedef double db;
const int inf = 0x3f3f3f3f;
const LL INF = 1e18;
const int maxn = 1e5 + 10;
using namespace std;
const int mod = 998244353;
int readint() {int x; scanf("%d", &x); return x;}
LL a[maxn], dis[maxn];
int n, k;
bool check(LL len) { //判断当前长度是否合法
int cnt = 0;
for(int i = 2; i <= n; i++) {
if (dis[i] > len) {
cnt += dis[i] / len;
if (dis[i] % len == 0) cnt--;
}
}
return cnt <= k;
}
int main() {
n = readint();
k = readint();
LL maxx = -1;
a[0] = 0;
for(int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
sort(a + 1, a + 1 + n);
for(int i = 2; i <= n; i++) {
dis[i] = a[i] - a[i - 1];
maxx = max(maxx, dis[i]);
}
LL left = 1, right = maxx + 1;
LL ans;
while (left < right) {
LL mid = (left + right) / 2;
if (check(mid)) {
ans = mid;
right = mid;
} else {
left = mid + 1;
}
}
printf("%lld", ans);
return 0;
}
补题
B 小宝的幸运数组
解题思路
先处理出前缀和数组 s s s,由 ( s [ i ] − s [ j ] ) % q = 0 (s[i] - s[j]) \% q = 0 (s[i]−s[j])%q=0 得到 s [ i ] % q = s [ j ] % q s[i] \% q = s[j] \% q s[i]%q=s[j]%q。因此只要找距离最大的两个满足条件的前缀和即可。
采用在线处理的方式,更新答案。
参考代码
#include<bits/stdc++.h>
typedef long long LL;
typedef double db;
const int inf = 0x3f3f3f3f;
const LL INF = 1e18;
const int maxn = 1e5 + 10;
using namespace std;
int readint() {int x; scanf("%d", &x); return x;}
struct node{
int start, len;
node() {len = 0;}
}s[maxn];
bool vis[maxn]; //标记某个数的起点
LL pre[maxn]; //前缀和数组
void init() {
memset(vis, 0, sizeof(vis));
memset(s, 0, sizeof(s));
pre[0] = 0;
}
int main() {
int t = readint();
while (t--) {
init();
int n = readint(), k = readint();
int ans = -1;
for(int i = 1; i <= n; i++) {
pre[i] = pre[i - 1] + 1LL * readint();
}
for(int i = 0; i <= n; i++) {
LL x = pre[i];
int res = x % k;
if (!vis[res]) {
vis[res] = true;
s[res].start = i;
} else {
s[res].len = max(s[res].len, i - s[res].start);
ans = max(s[res].len, ans);
}
}
printf("%d\n", ans);
}
return 0;
}
本文集合了多种算法题目,包括双指针、博弈论、数据查询、字符串处理、数学模拟等多个方面,深入浅出地解析了解题思路和关键技巧,并提供了参考代码。通过这些实例,读者可以提升算法理解和应用能力。

被折叠的 条评论
为什么被折叠?



