考试总结 - 大合集 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\) ,有:

  1. 向目前方向移动 \(x\) 单位距离。
  2. 顺时针转 \(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\) 的变化:

\[v = (v + rd)\bmod 4 \]

\(\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\) 。

  1. 对于 \(S_1\) ,可以建立一个大根堆,记录要去点的时间;
  2. 对于 \(S_2...S_n\) ,在大根堆里导入 \(t_i\) , \(sum = sum+x_i\) ;
  3. 若对总和 \(sum\) 大于 \(m-x_i\) (此时最大留给 \(t_1...t_i\) 的时间),持续弹出堆顶元素。

最后 \(sum\) 打擂台,就没了。

\(\textsf{code}\)

  1. 手写堆

    1. 建堆
     LL prq[n];
     int tot;
    
    1. 查询左儿子,右儿子,父亲
     int lp(int x){ return x << 1; }
     int rp(int x){ return x << 1 | 1; }
     int fa(int x){ return x >> 1; }
    
    1. 堆顶,长度,是否为空
     LL top(){ return prq[1]; }
     int size(){ return tot; }
     bool empty(){ return !tot; }
    
    1. 导入
     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;
     	}
     }
    
    1. 弹出
     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--;
     }
    
  2. 主函数

#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\) ,可以执行任意次以下操作:

  1. 将 \(a_1\) 插入 \(a_n\) 之后。
  2. 设此时第一个数为 \(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}\) 最大。

转化公式:

\[dp_{j+L_i} = \max(dp_{j+L_i},dp_j+D_i) \]

没了。
时间复杂度:\(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}\) ,没有发现这道题的隐含特点。

posted @ 2026-10-05 19:26  qjy123  阅读(4)  评论(0)    收藏  举报