2024年3月ZZUACM 招新赛题解

2024年3月ZZUACM 招新赛

题号题目
A区间次大值
B上课签到
C魔法森林(一)
D魔法森林(二)
ELOP
F跳格子
G猜数字
H抽卡记录
I安达的二维矩阵
J安达的数字手术
K跳楼梯
L前缀和

A 区间次大值—循环/签到题

题目描述

给定一个 n n n的全排列 a i a_i ai,下标为 1 − n 1-n 1n,请你输出所有 l < r l<r l<r的区间 [ l , r ] [l,r] [l,r]中次大数之和。

n n n的全排列指一个长为 n n n的数组, 1 , 2... N 1,2...N 1,2...N每个数字出现且只出现一次。

输入格式

第一行一个整数表示 n n n

第二行 n n n个整数表示 n n n的全排列

输出格式

输出一行一个正整数表示答案

输入输出样例

样例输入 #1
4
4 1 3 2
样例输出 #1
12
样例解释 #1

区间 [ 1 , 2 ] [1,2] [1,2]的次大值为 1 1 1,区间 [ 1 , 3 ] [1,3] [1,3]的次大值为 3 3 3,区间 [ 1 , 4 ] [1,4] [1,4]的次大值为 3 3 3,区间 [ 2 , 3 ] [2,3] [2,3]的次大值为 1 1 1,区间 [ 2 , 4 ] [2,4] [2,4]的次大值为 2 2 2,区间 [ 3 , 4 ] [3,4] [3,4]的次大值为 2 2 2。因此答案为 1 + 3 + 3 + 1 + 2 + 2 = 12 1+3+3+1+2+2=12 1+3+3+1+2+2=12

数据范围与约定

数据保证 n ≤ 1 0 3 n\leq 10^3 n103 1 ≤ a i ≤ n 1\leq a_i \leq n 1ain。如果 i ≠ j i\neq j i=j,则 a i ≠ a j a_i \neq a_j ai=aj

题解

先看数据范围可二层循环,可直接暴力,第一层循环代表左端点,第二层右端点,维护一个最大值和次大值即可

#include <iostream>
#include <vector>
#include <algorithm>using namespace std;int main() {int n; cin >> n;vector<int> a(n + 1, 0);for(int i = 0; i < n; i ++) cin >> a[i];int res = 0;for(int i = 0; i < n - 1; i ++) {int max1 = max(a[i], a[i + 1]);int max2 = min(a[i], a[i + 1]);res += max2;for(int j = i + 2; j < n; j ++) {            if(a[j] > max1) {max2 = max1;max1 = a[j];} else if(a[j] > max2) {max2 = a[j];}res += max2;}}cout << res << endl;
}

B 上课签到—二分+最短路

题目描述

某一天飞云从宿舍起床,但他上课快迟到了,他想要尽可能快的到达教室。他要在上课之前到达教室签到,换句话说,如果还有 h h h分钟上课,他必须要在小于等于 h h h分钟内到达教室。现在将宿舍,十字路口,教室抽象成一张无向图,含有 n n n个点和 m m m条边。由于路况的不同,每到达一个点都要消耗 a i a_i ai体力值(起始位置和终止位置也算),每经过一条边需要 w w w分钟。而对于飞云来说,由于体力可以恢复,所以他只需要知道路径上的最大体力消耗。现在飞云向聪明的你求助,在不迟到的情况下,所选路径中最大体力消耗的最小值是多少。

输入格式

第一行读入五个数 n , m , s t , e d , h n,m,st,ed, h n,m,st,ed,h(分别无向图的点数,边数,起始位置,终止位置,距离上课所剩的时间(单位:分钟))

接下来n行分别读入 n n n个数 a i a_i ai(每个点消耗的体力值)

接下来m行读入 x , y , w x,y,w x,y,w(分别代表无向边的两点和路上所花费的时间)

输出格式

输出一行代表最大消耗体力的最小值,若会迟到,则输出 − 1 -1 1

输入输出样例

样例输入 #1
4 4 1 4 8
8
5
6
10
1 3 4
2 4 1
2 1 2
3 4 9
样例输出 #1
10

样例解释

只能选择路径1->2->4,花费 3 3 3分钟,路径上最大体力消耗是到达 4 4 4的时候,花费 10 10 10.

数据范围与约定

$1 \le n \le 10^4 $, 1 ≤ m ≤ 2 ∗ 1 0 4 1 \le m\le 2*10^4 1m2104 1 ≤ a i , z , h ≤ 1 0 7 1 \le a_i,z, h \le 10^7 1ai,z,h107 1 ≤ x , y ≤ n 1 \le x,y \le n 1x,yn

题解

首先,对于最大…的最小,都可以考虑二分思路。其次,对于双权值问题,如果可以开二维,是可以跑二维的,但是明显这题开不了二维(会爆栈和TLE),并且有单调性质,所以考虑二分。

单调性质:设最大体力消耗是x,如果x满足条件(即可以找到一条路径,路径的权值和小于等于h并且点权值都小于等于x),那么大于x的也满足条件。所以考虑二分。

然后判断是不是满足跑一下dijkstra最短路即可。

#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
#define PII pair<int, int>
#define int long longconst int N = 1e4 + 10;
int a[N];struct edge {int v, w;
};
vector<edge> e[N];
int n, m, st, ed, h;
int dist[N];
int vis[N];bool check(int x) { // Dijkstra最短路for (int i = 1; i <= n; i++) {dist[i] = 1e18;vis[i] = 0;}priority_queue<PII, vector<PII>, greater<PII>> q;q.push({0, st});dist[st] = 0;if (a[st] > x) return false;while (q.size()) {auto now = q.top();q.pop();int u = now.second;if (vis[u]) continue;vis[u] = 1;for (auto t: e[u]) {int v = t.v, w = t.w;if (dist[v] > dist[u] + w && a[v] <= x) {dist[v] = dist[u] + w;q.push({dist[v], v});}}}return dist[ed] <= h;
}signed main() {cin >> n >> m >> st >> ed >> h;for (int i = 1; i <= n; i++) {cin >> a[i];}for (int i = 1; i <= m; i++) {int u, v, w; cin >> u >> v >> w;e[u].push_back({v, w});e[v].push_back({u, w});}int l = 1, r = 1e7 + 10;while (l < r) {int mid = l + r >> 1;if (check(mid)) r = mid;else l = mid + 1;}if (l == 1e7 + 10) cout << -1 << '\n';else cout << l << '\n';
}

C 魔法森林(一)—模拟

题目描述

小G来到了向往已久的魔法森林,并且想要探索其中的宝藏。

具体的,魔法森林可以看作是一个 n × m n\times m n×m的矩阵,其中矩阵的每个位置都可以用一对坐标表示,例如 ( i , j ) (i,j) (i,j) i i i行第 j j j列的位置。魔法森林中每个位置的具体信息可以用一个字符来表示:

若当前位置字符是’.',则表示当前位置为空地,小G可以自由行走。

若当前位置字符是’#',则表示当前位置为障碍,小G不可以通过。

另外作为魔法森林,魔法传送阵是其特色,具体的来说若字符是大写的英文字母,则表示当前位置为魔法传送阵,可以保证的是魔法传送阵都是成对存在的,也就是说某个英文字母表示的魔法传送阵一旦出现,一定会在魔法森林中出现两个,且只会出现两个。所以,对应于 26 26 26个大写的英文字母,魔法森林中也最多出现 26 26 26对传送阵,相应的传送阵之间是有强制传送的功能的,也就是说,当小G走到某个魔法传送阵时,会被自动传送到该传送阵所对应的字母的另一个传送阵的位置。(注意只有当从其他位置走向传送阵所在的格子时,魔法传送阵才会生效。)

已知,小G无法进入障碍所在的位置,也无法走出魔法森林(即如果小G下一步要到的位置是障碍或要超出边界时,则小G会提供留在原地)。

现给定魔法森林分布情况,小G的起始位置和将要行走的指令(一个只包含 L , R , U , D L,R,U,D L,R,U,D的字符串,分别表示向左,右,上,下的指令)。请回答小G最后所在的位置。

PS:请注意,传送阵是强制性传送的。

输入格式

第一行两个由空格分隔的整数表示 n n n m m m,分别表示魔法森林的行数和列数。

接下来的 n n n行,每行一个长为 m m m的字符串,代表该魔法森林的情况说明。

接下来的一行,两个由空格分隔的整数表示 x x x y y y,分别表示小G初始所在的位置。(数据保证,小G初始时所在的位置一定是’.')

接下来的一行,一个整数 l l l,表示接下来下小G需要行走的指令数。

最后一行,有一个长度为 l l l的字符串构成,代表小G接受到的指令数。

输出格式

一行两个整数,表示小G最终所在的位置。

输入输出样例

样例输入 #1
3 3 
.#.
A.A
#.#
1 1
5
RDLRD
样例输出 #1
2 1

数据范围与约定

数据保证 n , m ≤ 5000 n,m\leq 5000 n,m5000 1 ≤ l ≤ 1 0 6 1\leq l \leq 10^6 1l106

题解

按照指令字符串模拟将要走到的位置即可

可能的难点在于 大写字母的传送 模拟,可以用vector把2个传送坐标记录下来,每次模拟下一个走到的位置时,如果遇到大写字母(传送阵),从vector中找到与当前坐标不同的另一个坐标即为传送后的位置,模拟即可

#include <iostream>
#include <cstring>
#include <vector>using namespace std;
const int N = 5010;
char g[N][N];
vector<pair<int,int>> vec[30];
signed main() {int n, m; cin >> n >> m;for(int i = 1; i <= n; i ++) {for(int j = 1; j <= m; j ++) {cin >> g[i][j];if(g[i][j] >= 'A' && g[i][j] <= 'Z') {vec[g[i][j] - 'A'].push_back({i, j}); }}}int x, y; cin >> x >> y;int k; cin >> k;string s; cin >> s;for(int i = 0; i < k; i ++) {int tox = x, toy = y;if(s[i] == 'L') toy --;else if(s[i] == 'R') toy ++;else if(s[i] == 'U') tox --;else tox ++;if(tox < 1 || toy < 1 || tox > n || toy > m) continue;if(g[tox][toy] == '#') continue;if(g[tox][toy] == '.') {x = tox; y = toy;} else { // 传送int pos = g[tox][toy] - 'A';if(tox == vec[pos].front().first && toy == vec[pos].front().second) {tox = vec[pos].back().first;toy = vec[pos].back().second;} else {tox = vec[pos].front().first;toy = vec[pos].front().second;} x = tox; y = toy;}}cout << x << " " << y << endl;
} 

D 魔法森林(二)—bfs

题目描述

小G来到了向往已久的魔法森林,并且想要探索其中的宝藏。

具体的,魔法森林可以看作是一个 n × m n\times m n×m的矩阵,其中矩阵的每个位置都可以用一对坐标表示,例如 ( i , j ) (i,j) (i,j) i i i行第 j j j列的位置。魔法森林中每个位置的具体信息可以用一个字符来表示:

若当前位置字符是’.',则表示当前位置为空地,小G可以自由行走。

若当前位置字符是’#',则表示当前位置为障碍,小G不可以通过。

另外作为魔法森林,魔法传送阵是其特色,具体的来说若字符是大写的英文字母,则表示当前位置为魔法传送阵,可以保证的是魔法传送阵都是成对存在的,也就是说某个英文字母表示的魔法传送阵一旦出现,一定会在魔法森林中出现两个,且只会出现两个。所以,对应于 26 26 26个大写的英文字母,魔法森林中也最多出现 26 26 26对传送阵,相应的传送阵之间是有强制传送的功能的,也就是说,当小G走到某个魔法传送阵时,会被自动传送到该传送阵所对应的字母的另一个传送阵的位置。(注意只有当从其他位置走向传送阵所在的格子时,魔法传送阵才会生效。)

已知,小G无法进入障碍所在的位置,也无法走出魔法森林(即如果小G下一步要到的位置是障碍或要超出边界时,则小G会提供留在原地)。

现给定魔法森林的情况,小G所在的起点和终点,如果小G每次移动都将耗费一点体力,且传送阵并不消耗体力,请问小G要从起点到终点最少耗费的体力数?(若无法到达则输出-1)

PS:请注意,传送阵是强制性传送的。

输入格式

第一行两个由空格分隔的整数表示 n n n m m m,分别表示魔法森林的行数和列数。

接下来的 n n n行,每行一个长为 m m m的字符串,代表该魔法森林的情况说明。

接下来的一行,四个由空格分隔的整数表示 x 1 x_1 x1 y 1 y_1 y1, x 2 x_2 x2 y 2 y_2 y2分别表示小G的起点和终点。(数据保证,小G起点和终点的位置一定是’.')。

输出格式

一行一个整数,表示小G所需耗费的最少体力数,若无法到达则输出-1。

输入输出样例

样例输入 #1
3 3 
.#..
A..A
#.#.
1 1 2 2
样例输出 #1
3

数据范围与约定

数据保证 n , m ≤ 5000 n,m\leq 5000 n,m5000

题解

最短路问题在路径权值相同时可以用bfs解决,可参考经典题目

这个题在经典题目的基础上多了一个传送阵,可采用与上一题一样的方式模拟下一步走到的位置

#include <iostream>
#include <cstring>
#include <vector>
#include <queue>using namespace std;
const int N = 5010;
char g[N][N];
int vis[N][N];
int dist[N][N];
vector<pair<int,int>> vec[30];
int dx[] = {0, 1, 0, -1};
int dy[] = {1, 0, -1, 0};
signed main() {memset(dist, 0x3f, sizeof dist);memset(vis, 0, sizeof vis);int n, m; cin >> n >> m;for(int i = 1; i <= n; i ++) {for(int j = 1; j <= m; j ++) {cin >> g[i][j];if(g[i][j] >= 'A' && g[i][j] <= 'Z') {vec[g[i][j] - 'A'].push_back({i, j}); }}}int x1, y1; cin >> x1 >> y1;int x2, y2; cin >> x2 >> y2;queue<pair<int,int>> q;q.push({x1, y1});dist[x1][y1] = 0;while(q.size()) {int x = q.front().first;int y = q.front().second;q.pop();if(vis[x][y]) continue;vis[x][y] = 1;for(int i = 0; i < 4; i ++) {int tox = x + dx[i], toy = y + dy[i];if (tox < 1 || toy < 1 || tox > n || toy > m) continue;if (vis[tox][toy] || g[tox][toy] == '#') continue;if (g[tox][toy] != '.') {int pos = g[tox][toy] - 'A';if(tox == vec[pos].front().first && toy == vec[pos].front().second) {tox = vec[pos].back().first;toy = vec[pos].back().second;} else {tox = vec[pos].front().first;toy = vec[pos].front().second;} }dist[tox][toy] = min(dist[tox][toy], dist[x][y] + 1);q.push({tox, toy});}}if(dist[x2][y2] == 0x3f3f3f3f) cout << -1 << endl;else cout << dist[x2][y2] << endl;
} 

E LOP—签到题

题目描述

P19E99最近迷上了一款叫猪蛋联盟(League of Pigegg)的游戏,他决定成为职业选手,于是开始看职业联赛。

在这里首先向大家介绍一下Best of x赛制,即BO x赛制:每次比赛最多进行 x x x (保证 x x x为奇数) 局,最先赢得 ⌈ x 2 ⌉ \lceil\frac{x}{2}\rceil 2x (即 x x x除以 2 2 2取上整) 局的队伍获胜。

League of Pigegg这款游戏的比赛中,比赛分为两方红色方蓝色方每次比赛采取BO n赛制,即每次比赛最多进行 n n n ,最先赢得 ⌈ n 2 ⌉ \lceil\frac{n}{2}\rceil 2n 的队伍获胜。而每一的胜负判定又采用BO m赛制,即每局最多进行 m m m ,最先赢得 ⌈ m 2 ⌉ \lceil\frac{m}{2}\rceil 2m 的队伍拿下本的胜利。

而P19E99是个大笨蛋,他即不知道 n n n 等于多少,也不知道 m m m 等于多少,作为一名注重结果的小朋友,他只知道每把比赛哪一方获得了胜利,而作为一名伸手党,他想问你究竟哪一方获得了本次比赛的胜利。

输入格式

一行一个字符串 S S S,只包括 ‘R’‘B’ ,代表每把比赛的获胜方。

输出格式

一行一个字符 ‘R’‘B’,代表本次比赛的获胜方

输入输出样例

样例输入 #1
RRBBRBRBRBBBRB
样例输出 #1
B
样例输入 #2
RRBRBRBBBRBBRB
样例输出 #2
B

样例解释

样例 #1中, n = 3 , m = 5 n=3,\ m=5 n=3, m=5RRBBR后红色方率先赢得三比赛,拿下一,红色方胜利局数 1 1 1,之后的BRBRB中蓝色方率先赢得三比赛,拿下第二,蓝色方胜利局数 1 1 1,最后BBRB中,蓝色方率先赢得三比赛,胜利局数变为 2 2 2,赢得了本次比赛,成为获胜方。

数据范围与约定

数据保证 0 < ∣ S ∣ ≤ 1 0 6 0<|S|\leq 10^6 0<S106,且对局有效。

题解

多读几遍题,发现题目就是完全误导你往游戏规则上思考,但这个题与游戏规则完全无关

解法一:到最后一个字符才出现胜负,证明在最后一个字符之前还无法判断胜负,最后一个字符决定了胜负,所以直接判断最后一个字符即可

#include <iostream>
#include <cstring>using namespace std;signed main() {string s; cin >> s;cout << s.back() << endl;
} 

解法二:胜者的条件是 每局胜的把数最多和总体胜的局数最多,因此胜利的一定是字符出现最多的那个

#include <iostream>
#include <cstring>using namespace std;signed main() {string s; cin >> s;int sumr = 0, sumb = 0;for(int i = 0; i < s.size(); i ++) {sumr += s[i] == 'R';sumb += s[i] == 'B';}   if(sumr > sumb) cout << "R" << endl;else cout << "B" << endl;
} 

F 跳格子—dp

题目描述

阿昆(简称AKun)喜欢别人叫自己AK,更喜欢玩跳格子。 格子形状以及标号如下:

pic

Akun每次跳格子只能向着右方前进,即他脚下的格子标号只会不断增大,并且每步只能跳到相邻的格子上。

格子是无穷无尽的,永远没有终点。但AKun的体力有耗尽的时候,因此他给自己定了一个小目标,他认为只要达到了 n n n号格子就是胜利。 现在问AKun从1号格子出发,共有多少种方案能到达 n n n号格子。

Akun知道方案可能太多太多,所以他想让你输出总数量取模 1 0 9 + 7 10^9+7 109+7 之后的结果。

一个数 x x x y y y取模即 求 x x x整除以 y y y之后的余数 x % y x\%y x%y

输入格式

输入一行一个数字 n n n . 代表Akun最终要到达的格子的标号。

输出格式

输出一行一个数字 a n s ans ans, 代表方案数取模 1 0 9 + 7 10^9+7 109+7之后的结果。

输入输出样例

样例输入 #1

4

样例输出 #1

3

样例输入 #2

987654

样例输出 #2

530848436

样例解释

样例#1中,有如下三种方案:

1 2 3 41 2 41 3 4

数据范围与约定

( 2 ≤ n ≤ 1 0 6 ) (2 \le n\le 10^6) (2n106)

题解

经典的爬楼梯问题,可参考

因为只能朝右前方前进,因此每个数字的状态只能由他左边相邻的数字得到

即f[i] = f[i - 1] + f[i - 2], f[i]表示第i个数字的方案数

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
const int mod = 1e9 + 7;
signed main() {int n; cin >> n;vector<int> f(n + 1, 0);f[1] = 1; f[2] = 1;for(int i = 3; i <= n; i ++) {f[i] = (f[i - 1] + f[i - 2]) % mod;}cout << f[n] << endl;
} 

G 猜数字—期望+区间dp + 前缀和优化 + 乘法逆元

题目描述

这里有一个有趣的猜数字游戏。

给出 n n n 和一个小于等于 n n n 的正整数 x x x 。玩家不知道 x x x 具体是多少,只知道 n n n 1 ≤ x ≤ n 1\leq x\leq n 1xn

有一个随机数生成器(简称生成器),每次使用时会等概率生成在 [ l , r ] ( l ≤ r ) [l,r]\ (l\leq r) [l,r] (lr) 范围内的一个正整数( [ l , r ] [l,r] [l,r] 叫做生成范围), l l l r r r 在每次使用前由玩家自己选择,它们均为正整数。

玩家要使用生成器生成 x x x ,每次使用生成器生成一个数后(假设生成了 y y y ),如果 x = y x=y x=y 则游戏结束。

否则玩家会知道到 x x x y y y 的大小关系(大于或小于),玩家想要尽可能少的使用生成器,请问游戏结束前玩家使用生成器的期望次数是多少。

同时玩家是一个非常谨慎的人,如果他不能确定一个数是否为 x x x ,他就会把这个数包含在生成范围内。

答案对 998244353 998244353 998244353 取模。

输入格式

输入一行两个整数,分别表示 n , x n,x n,x

输出格式

输出一行一个正整数表示对 998244353 998244353 998244353 取模后的答案。

输入输出样例

样例输入 #1
2 1
样例输出 #1
499122178
样例解释 #1

一开始玩家将生成器范围设置为 [ 1 , 2 ] [1,2] [1,2] ,使用后有两种情况。

  1. 生成了 1 1 1 ,游戏直接结束。
  2. 生成了 2 2 2 ,玩家知道了 x x x 小于 2 2 2 ,之后将生成器范围设置为 [ 1 , 1 ] [1,1] [1,1] ,再次使用只会生成 1 1 1 ,游戏结束。

两种情况出现的概率均为 1 2 \frac{1}{2} 21 ,总使用次数的期望为 1 2 ∗ 1 + 1 2 ∗ 2 = 3 2 \frac{1}{2}*1+\frac{1}{2}*2=\frac{3}{2} 211+212=23 3 2 \frac{3}{2} 23 998244353 998244353 998244353 取模后为 499122178 499122178 499122178

样例输入 #2
2024 3
样例输出 #2
792613284

数据范围与约定

数据保证 1 ≤ x ≤ n ≤ 5000 1\leq x\leq n\leq 5000 1xn5000

题解

这题考点比较多,比较吃基本功。知识点:期望+区间dp + 前缀和优化 + 乘法逆元

对于期望问题,我一般令终态的状态作为起始状态,比如这题我让f[x][x]: 在[x,x]区间中选出x的期望。然后由小区间扩展到大区间,进行区间dp。

设区间f[i][j]: 区间[i,j]最终拿到x的期望。然后画一个数轴可以知道分三种情况套路。

1.如果拿的数是x,那么f[i][j] += 1 / (j - i + 1)

2.如果拿的数小于x,那么f[i][j] += ∑ (f[k][j] + 1)/ (j - i + 1) (k从i + 1 到 x)

3.如果拿的数大于x,那么f[i][j] += ∑(f[i][k] + 1) / (j - i + 1) (k从x到 j - 1)

对于∑这部分,可以发现对于同一个i和同一个j的状态来说可以提前保存下来,可以看我代码注释的部分,这是一个n^3的写法,更易理解。

最后就是乘法逆元,解决除法取模的问题,如果不知道可以去学习一下,这里我直接用的大数取余的模板。

#include <iostream>
#include <algorithm>
using namespace std;template<const int T>
struct ModInt {const static int mod = T;int x;ModInt(int x = 0) : x(x % mod) {}ModInt(long long x) : x(int(x % mod)) {}int val() { return x; }ModInt operator + (const ModInt &a) const { int x0 = x + a.x; return ModInt(x0 < mod ? x0 : x0 - mod); }ModInt operator - (const ModInt &a) const { int x0 = x - a.x; return ModInt(x0 < 0 ? x0 + mod : x0); }ModInt operator * (const ModInt &a) const { return ModInt(1LL * x * a.x % mod); }ModInt operator / (const ModInt &a) const { return *this * a.inv(); }bool operator == (const ModInt &a) const { return x == a.x; };bool operator != (const ModInt &a) const { return x != a.x; };void operator += (const ModInt &a) { x += a.x; if (x >= mod) x -= mod; }void operator -= (const ModInt &a) { x -= a.x; if (x < 0) x += mod; }void operator *= (const ModInt &a) { x = 1LL * x * a.x % mod; }void operator /= (const ModInt &a) { *this = *this / a; }friend ModInt operator + (int y, const ModInt &a){ int x0 = y + a.x; return ModInt(x0 < mod ? x0 : x0 - mod); }friend ModInt operator - (int y, const ModInt &a){ int x0 = y - a.x; return ModInt(x0 < 0 ? x0 + mod : x0); }friend ModInt operator * (int y, const ModInt &a){ return ModInt(1LL * y * a.x % mod);}friend ModInt operator / (int y, const ModInt &a){ return ModInt(y) / a;}friend ostream &operator<<(ostream &os, const ModInt &a) { return os << a.x;}friend istream &operator>>(istream &is, ModInt &t){return is >> t.x;}ModInt pow(int64_t n) const {if(n == 0) return 1;ModInt res(1), mul(x);while(n){if (n & 1) res *= mul;mul *= mul;n >>= 1;}return res;}ModInt inv() const {int a = x, b = mod, u = 1, v = 0;while (b) {int t = a / b;a -= t * b; swap(a, b);u -= t * v; swap(u, v);}if (u < 0) u += mod;return u;}};
using mint = ModInt<998244353>;
const int N = 5e3 + 10;
mint fact[N], invfact[N];
void init(){fact[0] = invfact[0] = 1;for(int i = 1; i < N; i ++) fact[i] = fact[i - 1] * i;invfact[N - 1] = fact[N - 1].inv();for(int i = N - 2; i; i --)invfact[i] = invfact[i + 1] * (i + 1);
}
inline mint C(int a, int b){if (a < 0 || b < 0 || a < b) return 0;return fact[a] * invfact[b] * invfact[a - b];
}mint f[N][N];
mint s[N][N]; 
mint add[N];signed main() {int n, x; cin >> n >> x;f[x][x] = 0;mint t2 = 0;for (int i = x; i >= 1; i--) {mint t1 = 0;for (int j = x; j <= n; j++) {f[i][j] += (t1 + j - x) / mint(j - i + 1);f[i][j] += (add[j] + x - i) / mint(j - i + 1);f[i][j] += 1 / (mint)(j - i + 1);    t1 += f[i][j];add[j] += f[i][j];}}cout << f[1][n] <<'\n';
} 

H 抽卡记录—dp

题目描述

HS的抽卡记录可以用一个长度为 n n n的数列表示出来,序列的第 i i i个数 a i a_i ai表示第 i i i次出货用了 a i a_i ai抽,由于抽的次数比较多,HS的心情会有一定的波动,当一段连续的抽卡中,每次出货用的抽数严格递增时,HS会感到开心;每次出货用的抽数严格递减时,HS会感到难过。现在HS想逆天改命,试图从原先的抽卡记录中删掉若干个数,得到新的抽卡记录数列 b i b_i bi使HS心情改变的总次数,即开心变难过或者难过变开心的总次数不超过 k k k次,由于HS有强迫症,所以不希望新数列任意相邻的两个数相同,求数列 b i b_i bi的最长长度。

输入格式

第一行两个整数 n n n k k k

第二行 n n n个整数 a i a_i ai

输出格式

一个数表示所求数列的最长长度。

样例输入1
5 0
1 2 5 3 4
样例输出1
4
样例输入2
5 2
1 2 5 3 4
样例输出2
5

数据范围与约定

1 ≤ n ≤ 1000 1\leq n\leq1000 1n1000

0 ≤ k ≤ 10 0\leq k\leq 10 0k10

1 ≤ i ≤ n 1\leq i\leq n 1in

1 ≤ a i ≤ 100000 1\leq a_i \leq 100000 1ai100000

题解

先看数据范围,n <= 1e3且k <= 10。如果你写过最长上升子序列这个板子就知道这题大概率是dp。

考虑f[i][j][k]:以i结尾的子序列,上升转下降,下降转上升的次数不超过j次,且最后是以上升结尾或者下降结尾(0表示上升,1表示下降)的子序列最大长度。

第一层循环直接遍历数组即可,第二层循环遍历上升转下降,下降转上升的次数(有种类似背包的思路),第三层循环遍历前i个数的下标。

dp方程看代码自行理解。

这题难点不在于dp的递推式推导,在于dp状态的设计(状态有点多,需要理清楚)。

#include <iostream>
using namespace std;const int N = 1e3 + 10;
int f[N][N][2];
int a[N];signed main() {int n, k; cin >> n >> k;for (int i = 1; i <= n; i++) cin >> a[i];int ans = 0;for (int i = 1; i <= n; i++) {for (int j = 0; j <= k; j++) {f[i][j][0] = f[i][j][1] = 1;for (int h = 1; h < i; h++) {if (a[i] > a[h]) {f[i][j][1] = max(f[i][j][1], f[h][j][1] + 1);if (j >= 1) {f[i][j][1] = max(f[i][j][1], f[h][j - 1][0] + 1);}}if (a[i] < a[h]) {f[i][j][0] = max(f[i][j][0], f[h][j][0] + 1);if (j >= 1) {f[i][j][0] = max(f[i][j][0], f[h][j - 1][1] + 1);}}}ans = max(ans, f[i][j][0]);ans = max(ans, f[i][j][1]);}}cout << ans << '\n';
}

I 安达的二维矩阵—签到题

题目描述

给你一个大小为 n × m n \times m n×m的二进制矩阵 g g g ,请你找出包含最多1的行的下标以及这一行中1的数目。
如果有多行包含最多的1,只需要选择 行下标最小 的那一行。

输入格式

第一行两个由空格分隔的整数表示 n n n m m m

接下来 n n n行,每行 m m m个整数,代表矩阵的元素

输出格式

输出 2 2 2个数,分别是包含最多1的行的下标和这一行中1的数目,中间用一个空格隔开。

输入输出样例

样例输入 #1
3 3
1 0 1 
1 1 1 
0 1 1
样例输出 #1
2 3
样例输入 #2
2 2
1 0 
0 1 
样例输出 #2
1 1

数据范围与约定

数据保证 1 ≤ n , m ≤ 1 0 3 1\leq n,m\leq 10^3 1n,m103 0 ≤ g [ i ] [ j ] ≤ 1 0\leq g[i][j] \leq 1 0g[i][j]1

题解

全场最简单的题,没什么好说的,直接遍历记录最大值即可

#include <iostream>
#include <vector>
#include <algorithm>using namespace std;int main() {int g[1010][1010] = {0};int n, m; cin >> n >> m;for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) cin >> g[i][j];int ans = 0, ans_x = 0, sum = 0;for(int i = 1; i <= n; i ++) {sum = 0;for(int j = 1; j <= m; j ++)if(g[i][j] == 1) ++ sum;if(sum > ans) {ans = sum;ans_x = i;}}cout << ans_x << " " << ans << endl;
}

J 安达的数字手术—贪心

题目描述

给你一个长度为 n n n的以字符串表示的非负整数 n u m num num ,移除这个数中的 1 1 1 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。

如果所有数字都被移除则输出 0 0 0

如果删除的数字是第一位,但第二位是0,则结果把第二位0去掉,即结果不含前导0

如9011,答案为11

输入格式

第一行表示字符串长度 n n n

第二行表示长度为 n n n的数字字符串

输出格式

输出 1 1 1行,表示移除 1 1 1位数字后最小的数字

样例输入 #1
5
29833
样例输出 #1
2833
样例输入 #2
1
9
样例输出 #2
0

数据范围与约定

数据保证 1 ≤ n ≤ 1 0 6 1\leq n \leq 10^6 1n106 n u m num num 仅由若干位数字(0 - 9)组成且除 0 0 0本身外, n u m num num不含任何前导零。

题解

先说结论,从左往右找到第一个a[i] > a[i+1]的位置把a[i]删除即可,如果不存在则删除最后一位

原理:(可参考此题目的升级版)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;int main() {int n; cin >> n;string s; cin >> s;string res = "";for(int i = 0; i < s.size(); i ++) {if(s[i] > s[i + 1]) {res = s.substr(0, i) + s.substr(i + 1);break;}}if(res.size() == 0) res = s.substr(0, s.size() - 1);// 除去前导0int pos = 0;while(res[pos] == '0' && pos < n) pos ++;res = res.substr(pos);// 特判 10000 和 个位数情况if(n == 1 || res.size() == 0) res = "0";cout << res << endl;
}

K 跳楼梯—找规律

题目描述

飞云现在终于到达了教室,他需要爬 n n n层楼梯,假设他现在处于第 x x x层,正在进行第 k k k次操作,可以做出以下两种操作之一:

  • 移动到 x + k x+k x+k
  • 移动到 x − 1 x -1 x1

假设这栋楼无限大(意味着有负无穷和正无穷层),试求到达第 n n n层的最小操作次数。

最开始飞云处在第 0 0 0层。

输入格式

第一行一个整数 t t t表示测试案例数 ( 1 ≤ t ≤ 1000 ) (1\le t \le 1000) (1t1000)

每一个测试案例读入一个整数 n n n表示楼层数。 ( 1 ≤ n ≤ 1 0 6 ) (1 \le n \le 10^6) (1n106)

输出格式

输出一行一个正整数表示答案

输入输出样例

样例输入 #1
5
1
2
3
4
5
样例输出 #1
1
3
2
3
4

样例解释

在第一个测试案例,只需要1次,从0跳到1.
在第二个测试案例,至少需要三次。
第一次,从0跳到1.
第二次,从1跳到3
第三次,从3跳到2。

数据范围与约定

数据保证 1 ≤ t ≤ 1000 , 1 ≤ n ≤ 1 e 6 1 \le t \le 1000, 1 \le n \le 1e6 1t1000,1n1e6.

题解

这题可以理解为 1+2+3+4+…+k(代表k次操作)一直加到大于等于n后,可以把其中任意几位数字变为-1加起来使其等于n

我们会发现一个规律 这k个数之和sum,一定可以在k次操作后凑出 1+2+3+…+k-1之和 + 1 到 sum - 2

为什么不是sum-1呢,因为我们把1变为-1后,相当于-2了

比如 28 = 1 + 2 + 3 + 4 + 5 + 6 + 7

26 可以由 -1 + 2 + 3 + 4 + 5 + 6 + 7,7次操作即可

25 可以由 1 + -1 + 3 + 4 + 5 + 6 + 7,7次操作

24 可以由 1 + 2 + -1 + 4 + 5 + 6 + 7,7次操作

但27 因为大于 sum - 2 所以不可以只能由8次操作

如 1 + 2 + 3 + 4 + 5 + 6 + 7 + -1,8次操作组成

#include <iostream>
#include <cstring>
using namespace std;void solve() {int n; cin >> n;int res, sum = 0;for(int i = 1; i <= 2000; i ++) {sum += i;if(sum == n || sum - 1 > n) {res = i;break;}}cout << res << endl;
}signed main() {int T; cin >> T;while(T --) solve();
}

L 前缀和-二分+前缀和

题目描述

给定一个长为 n n n的数组 a i a_i ai,保证 a i ≥ 0 a_i\geq 0 ai0

接下来 q q q次询问,每次询问给定 s u m i sum_i sumi,请你输出最小的 r r r,满足 1 ≤ r ≤ n 1\leq r\leq n 1rn a 1 + a 2 + . . . + a r ≥ s u m i a_1+a_2+...+a_r\geq sum_i a1+a2+...+arsumi,也就是 ∑ j = 1 r a j ≥ s u m i \sum_{j=1}^r a_j \geq sum_i j=1rajsumi。如果不存在这样的 r r r,请输出 − 1 -1 1

输入格式

第一行两个由空格分隔的整数表示 n n n q q q

第二行 n n n个整数表示数组 a i a_i ai

接下来 q q q行,每行一个整数表示一次询问。

输出格式

输出 q q q行,对于第 i i i行,如果有答案,请输出最小的满足要求的 r r r;如果没有答案,请输出 − 1 -1 1

输入输出样例

样例输入 #1
3 7
1 2 3
0
1
2
3
4
5
6
7
样例输出 #1
1
1
2
2
3
3
3
-1

数据范围与约定

数据保证 n , q ≤ 1 0 6 n,q\leq 10^6 n,q106 0 ≤ a i ≤ 1 0 6 0\leq a_i \leq 10^6 0ai106 0 ≤ s u m i ≤ 1 0 18 0\leq sum_i\leq 10^{18} 0sumi1018

题解

对于最大…的最小且存在单调性的问题,都可以考虑二分思路

单调性, a 1 + a 2 + . . . + a r a_1+a_2+...+a_r a1+a2+...+ar 一定是越加越大,因此直接二分答案即可

#include <iostream>
#include <cstring>
#include <vector>using namespace std;
signed main() {int n, q; scanf("%d %d",&n, &q);vector<int> a(n + 1, 0);vector<long long> s(n + 1, 0);for(int i = 1; i <= n; i ++) {scanf("%d", &a[i]);s[i] = s[i - 1] + a[i];}int l, r;long long sum;while(q --) {l = 1, r = n;scanf("%lld", &sum);while(l < r) {int mid = l + r >> 1;if(s[mid] >= sum) r = mid;else l = mid + 1; }if(s[n] < sum) cout << -1 << '\n';else cout << r << '\n';}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/732816.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

外包干了10天,技术退步明显···

先说一下自己的情况&#xff0c;本科生&#xff0c;通过校招进入杭州某软件公司&#xff0c;干了接近3年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试&…

【鸿蒙开发】第十七章 Web组件(一)

1 Web概述 Web组件用于在应用程序中显示Web页面内容&#xff0c;为开发者提供页面加载、页面交互、页面调试等能力。 页面加载&#xff1a;Web组件提供基础的前端页面加载的能力&#xff0c;包括&#xff1a;加载网络页面、本地页面、html格式文本数据。 页面交互&#xff1a…

JAVA使用的工具类-Hutool

文章目录 Hutool工具类简介1&#xff1a;身份证工具类相关方法1.1 身份证脱敏处理 字符串补零处理(此处是JAVA类的方法&#xff0c;并无引用StrUtil)springboot前后端分离&#xff0c;后端返回json字符串带斜杠问题处理 在这里整理一下博主常用的工具类方法-hutool工具类,这里囊…

制定一份完美的测试计划,让您的产品质量更上一层楼!

大家好&#xff0c;我是彭于晏。今天学习测试计划如何书写。 虽然很多人日常工作中都知道测试计划是什么&#xff0c;但是写好测试计划&#xff0c;其实并不容易。今天就来一起学习下测试计划如何书写。 什么是测试计划&#xff1f; 测试计划是一份为软件产品所准备的详细文档…

目标检测——监控下打架检测数据集

一、简述 首先&#xff0c;监控下打架检测是维护公共安全的重要手段。在公共场所、学校、监狱等地方&#xff0c;打架事件往往难以避免。通过安装打架检测监控系统&#xff0c;可以实时监控并准确识别打架事件&#xff0c;及时采取必要的应对措施&#xff0c;有效地减少打架事…

RNN预测正弦时间点

import torch.nn as nn import torch import numpy as np import matplotlib matplotlib.use(TkAgg) from matplotlib import pyplot as plt # net nn.RNN(100,10) #100个单词&#xff0c;每个单词10个维度 # print(net._parameters.keys()) #序列时间点预测num_time_steps 50…

java-ssm-jsp-基于ssm的宝文理学生社团管理系统

java-ssm-jsp-基于ssm的宝文理学生社团管理系统 获取源码——》公主号&#xff1a;计算机专业毕设大全

应对高并发的软件架构之道

在去年年终总结的时候&#xff0c;我提出了这样的困惑&#xff0c;究竟什么是真正的技术能力&#xff0c;是对于各种底层技术的钻研吗&#xff1f;钻研是好事&#xff0c;但实践下来&#xff0c;深入钻研并不在实际工作中有用&#xff0c;且钻研的越深&#xff0c;忘得越快&…

Leetcode : 1137. 高度检查器

学校打算为全体学生拍一张年度纪念照。根据要求&#xff0c;学生需要按照 非递减 的高度顺序排成一行。 排序后的高度情况用整数数组 expected 表示&#xff0c;其中 expected[i] 是预计排在这一行中第 i 位的学生的高度&#xff08;下标从 0 开始&#xff09;。 给你一个整数…

一篇搞懂什么是LRU缓存|一篇搞懂LRU缓存的实现|LRUCache详解和实现

LRUCache 文章目录 LRUCache前言项目代码仓库什么时候会用到缓存(Cache)缓存满了&#xff0c;怎么办&#xff1f;什么是LRUCacheLRUCache的实现LRUCache对应的OJ题实现LRUCache对应的STL风格实现 前言 这里分享我的一些博客专栏&#xff0c;都是干货满满的。 手撕数据结构专栏…

什么是测试用例?如何设计?

在学习或者实际的测试工作中经常都会提到“测试用例”这个词&#xff0c;没错&#xff0c;测试用例是测试工作的核心&#xff0c;不管要做的是什么样的测试&#xff0c;在真正动手执行测试之前&#xff0c;我们都需要先根据软件需求来设计测试用例&#xff0c;之后再依据设计好…

动态加权平衡损失:深度神经网络的类不平衡学习和置信度校准

系列文章目录 文章目录 系列文章目录前言一、研究目的二、研究方法创新点处理类不平衡的大多数方法交叉熵损失函数Brier Score 三、DWB Loss总结 前言 Dynamically Weighted Balanced Loss: ClassImbalanced Learning and Confidence Calibration of Deep Neural Networks 下载…

2024年3月10日 十二生肖 今日运势

小运播报&#xff1a;2024年3月10日&#xff0c;星期日&#xff0c;农历二月初一 &#xff08;甲辰年丁卯月癸酉日&#xff09;&#xff0c;法定节假日。 红榜生肖&#xff1a;龙、牛、蛇 需要注意&#xff1a;鸡、狗、兔 喜神方位&#xff1a;东南方 财神方位&#xff1a;…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Image)

Image为图片组件&#xff0c;常用于在应用中显示图片。Image支持加载PixelMap、ResourceStr和DrawableDescriptor类型的数据源&#xff0c;支持png、jpg、jpeg、bmp、svg、webp和gif类型的图片格式。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&am…

作业 字符数组-统计和加密

字串中数字个数 描述 输入一行字符&#xff0c;统计出其中数字字符的个数。 输入 一行字符串&#xff0c;总长度不超过255。 输出 输出为1行&#xff0c;输出字符串里面数字字符的个数。 样例 #include <iostream> #include<string.h> using namespace std; int m…

AI绘画提示词案例(宠物

目录 1. 雪地猫猫&#xff1a;1.1 提示词&#xff1a;1.2 效果&#xff1a; 2. 趴地猫猫&#xff1a;2.1 提示词&#xff1a;2.2 效果&#xff1a; 3. 长城萨摩耶&#xff1a;3.1 提示词&#xff1a;3.2 效果&#xff1a; 4. 沙发猫猫&#xff1a;4.1 提示词&#xff1a;4.2 效…

Mysql:如何自定义导出表结构

为了方便将mysql表结构信息快速录入到word或Excel表格中&#xff0c;最终实现如下效果&#xff1a; 对于word,则可将Excel表格复制粘贴即可。 废话不多少&#xff0c;开干。 准备准建&#xff1a;navicat 或sqlyog 第一步&#xff1a;编辑sql&#xff0c;如&#xff1a; SE…

HTML 01

1.html使用标签来表达 结束标签多一个/ <strong>文字内容</strong> <hr> 包裹内容就是双标签&#xff0c;换行等是单标签 浏览器中显示内容&#xff1a; 2.html的骨架是网页模板 <!DOCTYPE html> <html lang"en"> <head>&l…

Full GC的认识、预防和定位

(/≧▽≦)/~┴┴ 嗨~我叫小奥 ✨✨✨ &#x1f440;&#x1f440;&#x1f440; 个人博客&#xff1a;小奥的博客 &#x1f44d;&#x1f44d;&#x1f44d;&#xff1a;个人CSDN ⭐️⭐️⭐️&#xff1a;传送门 &#x1f379; 本人24应届生一枚&#xff0c;技术和水平有限&am…

【leetcode】429. N 叉树的层序遍历

题目描述 给定一个 N 叉树&#xff0c;返回其节点值的_层序遍历_。&#xff08;即从左到右&#xff0c;逐层遍历&#xff09;。 树的序列化输入是用层序遍历&#xff0c;每组子节点都由 null 值分隔&#xff08;参见示例&#xff09;。 示例 1&#xff1a; 输入&#xff1a;…