考试总结 - 大合集 1
收录
- 2025.8.24(写的太烂了,于是重写的一篇)
- 2026.4.11
text - 2025.8.24
签到题:sign
题目大意
有 \(n\) 个数 \(\{a_n\}\),从 \(a\) 内选择一些数,使这些数的和是 \(3\) 的倍数且最大,若无解则输出
cantget。数据范围:\(n < 10^{5}\)
思路
注(\(\textsf{2026.10.5}\)):这题太水了
设:
- \(\text{sum} = \displaystyle\sum a_i\),即 \(a\) 之和。
- \(\text{min11} = \displaystyle\min_{a_i\equiv 1 (\bmod 3)} a_i\),即 \(a\) 中模 \(3\) 余 \(1\) 的数的最小值。
- \(\text{min12} = \displaystyle\min_{\scriptsize\begin{array}{c} a_i\equiv 1 (\bmod 3) \\ i\neq\text{subscript(min11)} \end{array}} a_i\),即 \(a\) 中模 \(3\) 余 \(1\) 的数的非严格次小值。
- \(\text{min21} = \displaystyle\min_{a_i\equiv 2 (\bmod 3)} a_i\),即 \(a\) 中模 \(3\) 余 \(2\) 的数的最小值。
- \(\text{min22} = \displaystyle\min_{\scriptsize\begin{array}{c} a_i\equiv 2 (\bmod 3) \\ i\neq\text{subscript(min21)} \end{array}} a_i\),即 \(a\) 中模 \(3\) 余 \(2\) 的数的非严格次小值。
分类讨论:
当 \(\text{sum}\equiv 0 (\bmod 3)\):\(\text{ans} = \text{sum}\)。
当 \(\text{sum}\equiv 1 (\bmod 3)\):\(\text{ans} = \text{sum}-\min(\text{min11}, \text{min21+min22})\)。
当 \(\text{sum}\equiv 2 (\bmod 3)\):\(\text{ans} = \text{sum}-\min(\text{min11+min12}, \text{min21})\)。
特判就不写了,这是显然的。
text - 2026.4.11
\(\textsf{A robot U677084}\)
分数情况
考试分数:\(\texttt{100 pts}\)
考后分数:\(\texttt{100 pts}\)
题目大意
一个点,初始在 \((0,0)\),给定一个长度为 \(n\) 数列,对于每一个 \(a_x\) ,有:
- 向目前方向移动 \(x\) 单位距离。
- 顺时针转 \(90x^\circ\)。
数列循环 \(T\) 次后,问此点距原点的曼哈顿距离。
\(1\le n,T,a_i\le 5\times 10^5\)。
题目思路
由于每次循环都是一样的,只是初始方向不同。
所以分两步做:
1. 第一次循环
用一个 \(h\) 表示向上的距离, \(r\) 表示向右的距离, \(rd\) 表示方向,暴力 \(O(n)\) 。
2. \(T\) 次循环
用 \(highs\) 与 \(rights\) 统计总共向上的距离与总共向右的距离,\(v\)表示每一次循环的方向, \(v\) 的变化:
\(\text{highs}\) 与 \(\text{rights}\) 的变化:
- 若 \(v = 0\),\(\text{highs} = \text{highs}+h\),\(\text{rights} = \text{rights}+r\)。
- 若 \(v = 1\),\(\text{highs} = \text{highs}-r\),\(\text{rights} = \text{rights}+h\)。
- 若 \(v = 2\),\(\text{highs} = \text{highs}-h\),\(\text{rights} = \text{rights}-r\)。
- 若 \(v = 3\),\(\text{highs} = \text{highs}+r\),\(\text{rights} = \text{rights}-h\)。
总复杂度:\(O(n+T)\)
\(\textsf{code}\)
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10, rt = 4;
int n, T, rd, v;
long long coush[rt], h, r, highs, rights;
int main(){
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> T;
for(int i = 1, tmp; i <= n; i++){
cin >> tmp;
coush[rd] += tmp;
rd = (rd + tmp) % rt;
}
h = coush[0] - coush[2];
r = coush[1] - coush[3];
for(int i = 1; i <= T; i++){
if(v == 0) highs += h, rights += r;
else if(v == 1) highs -= r, rights += h;
else if(v == 2) highs -= h, rights -= r;
else highs += r, rights -= h;
v = (v + rd) % rt;
}
cout << abs(highs) + abs(rights);
return 0;
}
题目反思
其实没什么好反思的。
一开始就 \(\green{\textsf{AC}}\) 了。
但老师的做法 \(O(n)\) ,看来还要进一步加强。
\(\textsf{B plan P2107}\)
分数情况
考试分数:\(\texttt{0 pts}\)
考后分数:\(\texttt{100 pts}\)
题目大意
给定线上 \(n\) 个点 \(x_1,\dots,x_n\) ,初始时你在原点,给你 \(m\) 个时间,每一个时间可以移动一格,也可以在点 \(x_i\) 花 \(t_i\) 的时间完成一个点,问最多可以完成多少个点?
数据范围:\(1\le n\le 10^5,0\le m,x_i\le 10^{18},0\le t_i\le 10^9\)。
题目思路
一开始就想到 \(\texttt{dp}\),可 \(x_i\) 太大,炸了。
发现 \(x_i\) 均为正数,先排序,然后设 \(S_i\) 为对于前 \(i\) 个点的最优解,最后 \(max_{i\in \{1,n\}} S_i\) 为答案。
怎么计算 \(S_i\) ?
我们可以通过 \(S_{i-1}\) 推导出 \(S_i\) 。
- 对于 \(S_1\) ,可以建立一个大根堆,记录要去点的时间;
- 对于 \(S_2...S_n\) ,在大根堆里导入 \(t_i\) , \(sum = sum+x_i\) ;
- 若对总和 \(sum\) 大于 \(m-x_i\) (此时最大留给 \(t_1...t_i\) 的时间),持续弹出堆顶元素。
最后 \(sum\) 打擂台,就没了。
\(\textsf{code}\)
-
手写堆
- 建堆
LL prq[n]; int tot;- 查询左儿子,右儿子,父亲
int lp(int x){ return x << 1; } int rp(int x){ return x << 1 | 1; } int fa(int x){ return x >> 1; }- 堆顶,长度,是否为空
LL top(){ return prq[1]; } int size(){ return tot; } bool empty(){ return !tot; }- 导入
void push(LL x){ prq[++tot] = x; yc = tot; while(yc > 1){ if(prq[fa(yc)] < prq[yc]){ swap(prq[fa(yc)], prq[yc]); yc = fa(yc); } else break; } }- 弹出
void pop(){ prq[1] = 0; yc = 1; while(yc <= tot){ if(lp(yc) > tot) break; if(lp(yc) == tot){ swap(prq[lp(yc)], prq[yc]); break; } if(prq[lp(yc)] < prq[rp(yc)]) swap(prq[lp(yc)], prq[rp(yc)]); swap(prq[lp(yc)], prq[yc]); yc = lp(yc); } tot--; } -
主函数
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
typedef long long LL;
struct node { LL x, t; } a[N];
LL prq[N], sum, m;
int n, tot, yc, maxx = -1;
bool cmp(node A, node B){ return A.x < B.x; }
int main(){
// freopen("plan.in","r",stdin);
// freopen("plan.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; i++)
cin >> a[i].x >> a[i].t;
sort(a+1, a+n+1, cmp);
for(int i = 1; i <= n; i++){
push(a[i].t);
sum += a[i].t;
while(!empty() && sum > m - a[i].x){
sum -= top();
pop();
}
if(sum > m) break;
maxx = max(maxx, size());
}
cout << maxx;
return 0;
}
时间复杂度:\(O(n\log n)\)
题目反思
考试时光顾着看 \(\texttt{dp}\) 去了,写都写不出来。
其实换个角度,从小往大推,就可以想出来了。
\(\textsf{C card U677075}\)
分数情况
考试分数:\(\texttt{0 pts}\)
考后分数:\(\texttt{100 pts}\)
题目大意
有一个长度为 \(n\) 数列 \(a_1,\dots,a_n\) ,每个元素都有两个属性:\(L_i\) 与 \(D_i\) ,可以执行任意次以下操作:
- 将 \(a_1\) 插入 \(a_n\) 之后。
- 设此时第一个数为 \(a_i\),使分数 \(sum\) 增加 \(D_i\),删除数列的前 \(L_i\) 项。若数列不足 \(L_i\) ,不可执行此操作。
问能制造出最高的分数。
数据范围:\(1\le L_i\le n\le 1000,1\le D_i\le 10^6\) 。
题目思路
把这道题考虑成环上的问题,省去操作一,发现这题就是一个 \(\texttt{01}\) 背包 \(\texttt{dp}\) 的题目,设选择执行操作2的卡牌为 \(x_1,\dots,x_t\) ,则要让 \(L_{x_1}+\dots+L_{x_t}\le n\) 且 \(D_{x_1}+\dots+D_{x_t}\) 最大。
转化公式:
没了。
时间复杂度:\(O(n^2)\) 。
\(\textsf{code}\)
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, L[N], D[N], dp[N], vis[N], ot, maxx = -1;
int main(){
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++)
cin >> L[i];
for(int i = 1; i <= n; i++)
cin >> D[i];
vis[0] = 1;
for(int i = 1; i <= n; i++)
for(int j = min(ot, n-L[i]); j >= 0; j--)
if(vis[j]){
vis[j + L[i]] = 1;
dp[j + L[i]] = max(dp[j + L[i]], dp[j] + D[i]);
ot = max(ot, j + L[i]);
}
for(int i = 0; i <= n; i++)
maxx = max(maxx, dp[i]);
cout << maxx;
return 0;
}
题目反思
觉得第2题才是用 \(\texttt{dp}\) ,没有发现这道题的隐含特点。

浙公网安备 33010602011771号