2022 陕西 省赛

顺序按照我认为的难度排序

1|0J

1|1题目描述

有一个长度为 n 的数列 ai 。有一个数 k,表示可以将 k 个 ai 减 1,可以连续可以不连续。问你可以将所有的数都变成相同的数的 k 最大是多少。

1|2思路

k=1 的时候,相当于把一个数减去 1,k=n1 的时候,相当于将任意的数加上 1。所以除了所有的数都相等的情况答案为 n 之外,其他所有的情况都是 n1

1|3code

// // Created by kersen on 24-4-14. // // // Created by kersen on 24-4-14. // #include <bits/stdc++.h> #define N 100010 // using namespace std; int n, a[N]; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); int sum = 0; for (int i = 2; i <= n; i++) if (a[i] == a[1]) sum++; if (sum == n - 1) cout << n << "\n"; else cout << n - 1; return 0; }

2|0B

2|1题目描述

一个翻牌游戏。

给出两个长度为 n 的数列,分别叫做 ai bi 表示卡牌两面的数字大小。给出一个 k 表示可以将任意的 ai 变成 bi 的次数。变成 bi 之后就不能再变回 ai

Q 次询问,每次询问都会给出 m 个不可以翻转的卡牌,对于每次询问给出最大的牌上的数字之和。

2|2思路

对于所有的卡牌,求出 biai 表示为 ch ,并让卡牌按照 ch 由大到小排序。然后求出初始的翻 k 张牌的价值和。对于每次询问,会给出 m 张不可以翻的卡牌,让卡牌按照 ch 拍完序,然后从前往后枚举,如果这张牌在不可以翻的 k 张牌之内,就手动移除,更新一下价值。

2|3code

// // Created by kersen on 24-4-14. // #include <bits/stdc++.h> #define N 100010 #define lson rt << 1 #define rson rt << 1 | 1 #define int long long // using namespace std; int n, k, q, m; map<int, bool> ma; map<int, int> mb; struct node { int len, sum, lazy; }tree[N << 2]; struct PPP { int a, b, c, id; }num[N]; struct OOO { int id, pat; }qu[N]; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } bool cmp(PPP a, PPP b) { return a.c > b.c; } void push_up(int rt) { tree[rt].sum = tree[lson].sum + tree[rson].sum; } void build(int rt, int l, int r) { tree[rt].len = r - l + 1, tree[rt].lazy = 0; if (l == r) { if (l <= k) { tree[rt].sum = num[l].b; ma[num[l].id] = 1; } else tree[rt].sum = num[l].a; return; } int mid = (l + r) >> 1; build(lson, l, mid); build(rson, mid + 1, r); push_up(rt); } void change(int rt, int l, int r, int pos, int c) { if (l == r) { tree[rt].sum = c; return; } int mid = (l + r) >> 1; if (pos <= mid) change(lson, l, mid, pos, c); else change(rson, mid + 1, r, pos, c); push_up(rt); } int query(int rt, int l, int r, int L, int R) { if (L <= l && r <= R) return tree[rt].sum; int mid = (l + r) >> 1, ans = 0; if (L <= mid) ans += query(lson, l, mid, L, R); if (R > mid) ans += query(rson, mid + 1, r, L, R); return ans; } bool cmp1(OOO a, OOO b) { return a.pat < b.pat; } signed main() { n = read(), k = read(); for (int i = 1; i <= n; i++) num[i].a = read(); for (int i = 1; i <= n; i++) { num[i].b = read(); num[i].c = num[i].b - num[i].a; num[i].id = i; } sort(num + 1, num + n + 1, cmp); for (int i = 1; i <= n; i++) mb[num[i].id] = i; build(1, 1, n); int ans = tree[1].sum; q = read(); for (int i = 1; i <= q; i++) { m = read(); // cout << m << "\n"; queue<int> q, p; int thisk = k; for (int j = 1; j <= m; j++) { qu[j].id = read(); qu[j].pat = mb[qu[j].id]; // cout << qu[j].id << " " << qu[j].pat << "\n"; } // puts(""); sort(qu + 1, qu + m + 1, cmp1); // for (int j = 1; j <= m; j++) cout << qu[j].pat << "\n"; // puts(""); int thisans = ans; for (int j = 1; j <= m; j++) { // cout << qu[j].pat << "\n"; if (qu[j].pat <= thisk) { thisans -= num[qu[j].pat].b; thisans += num[qu[j].pat].a; q.push(qu[j].pat); thisk++; thisans -= num[thisk].a; thisans += num[thisk].b; p.push(thisk); } } printf("%lld\n", thisans); } return 0; }

3|0G

3|1题目描述

给出一个 n,m。表示石头数量和水的数量。在二维平面中用石头组成一个上方开口的容器。问你 n 个石头可以围出最多多少个水,m个水需要最少多少个石头围出。

3|2思路

纯纯画图猜结论题。

赛场上队友怕爆 unsigned long long 然后就开始用 python 写,结果因为 python 不熟练,出现了很多奇奇怪怪的问题,然后又换成 C++ 结果不会爆,这下纯纯小糖人了。

3|3code

// // Created by kersen on 24-4-14. // #include <bits/stdc++.h> #define int unsigned long long using namespace std; signed main() { int n, m; cin >> n >> m; int k = 0; int ans = 0; if (n % 2 == 0) { k = n / 2 - 1; ans = k * (k + 1); } else { k = (n - 1) / 2; ans = k * k; } if (ans >= m) { cout << ans; } else { int k1 = ceil((sqrt(4 * m + 1) - 1) / 2); int n1 = (k1 + 1) * 2; int k2 = ceil(sqrt(m)); int n2 = k2 * 2 + 1; cout << min(n1, n2) << '\n'; } return 0; }

4|0C

4|1题目描述

给出 n 个字符串和一个价值 k。要打出者所有的字符串。在打每个字符串的时候有两种选择。

1、手动把所有的字符串都敲出来,每敲一个字的价值是 1,这样的价值就是 lengthi

2、将之前所有的打过的字符串选择一个复制一下粘贴过来(有且仅能复制一次),这下的操作价值是 k 。然后对复制过来的字符串进行删减一个字符的价值是 1, 在任意的地方添加的价值也是 1 。

所有的字符串可以按照不同的顺序进行敲打。

问你将所有的字符串都打出来的最小价值是多少。

4|2思路

一个很浅显的道理是,可以先两两求出所有的字符串 i,j 之间的 lcs 那么如果已经打出了 si 那么用第二种选择的价值就是 lengthilcsij+lengthjlcsi,j

可以抽象的来想一下。

对于所有的字符串抽象成点,然后建立一个虚点,虚点和其他点连边的边权设置为各个字符串的长度,然后字符串与字符串之间的连边设置为上述价值。

然后对所有的点跑最小生成树就可以得到最小的价值。

4|3code

#include <bits/stdc++.h> #define N 110 #define int long long using namespace std; int n, k, add_edge, fa[N]; struct PPP { int len; char s[N]; }str[N]; struct node { int a, b, dis; } edge[N * N]; void add(int from, int to, int dis) { edge[++add_edge].a = from; edge[add_edge].b = to; edge[add_edge].dis = dis; } int get_dis(int x, int y) { int f[N][N]; for (int i = 0; i <= str[x].len; i++) for (int j = 0; j <= str[y].len; j++) f[i][j] = 0; for (int i = 1; i <= str[x].len; i++) for (int j = 1; j <= str[y].len; j++) if (str[x].s[i] == str[y].s[j]) f[i][j] = f[i - 1][j - 1] + 1; else f[i][j] = max(f[i - 1][j], f[i][j - 1]); return k + str[x].len + str[y].len - f[str[x].len][str[y].len] * 2; } bool cmp(node a, node b) { return a.dis < b.dis; } int father(int x) { if (x != fa[x]) fa[x] = father(fa[x]); return fa[x]; } signed main() { cin >> n >> k; for (int i = 1; i <= n; i++) { cin >> str[i].len; for (int j = 1; j <= str[i].len; j++) cin >> str[i].s[j]; // scanf("%s", str[i].s + 1); // cin >> len >> a[i]; add(n + 1, i, str[i].len); } for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (i != j) { add(i, j, get_dis(i, j)); } sort(edge + 1, edge + add_edge + 1, cmp); for (int i = 1; i <= n + 1; i++) fa[i] = i; int cnt = 0, dis = 0; for (int i = 1; i <= add_edge; i++) { int fx = father(edge[i].a), fy = father(edge[i].b); if (fx != fy) { fa[fx] = fy; cnt++; dis += edge[i].dis; } if (cnt == n) break; } cout << dis << "\n"; return 0; }

5|0D

5|1题目描述

多组询问。

每组给出一个 s。S是T的前缀,并且S与T的hash值相同。

问你T能是多少。

5|2思路

暴力枚举所有长度 6 的串以后会发现可以构造出所有不同的 hash 值,于是直接把原串向左移动 6 位, 给前面拼 个字母使得 hash 值不变即可。

直接暴力的话复杂度是不对的(主要是这个模数速度挺慢的),可以考虑考虑这样一个事实:既然这 6 位 的哈希值 X 是多少已知,我们直接枚举它是几倍的 mod+X,然后把他还原回原串,如果每一位都在 区间 [1-26]之间,就是一组合法解。这样的解非常密集,枚举一次的成功率大约是 (26/29)^6 = 51%, 所以只需要枚举几个数就能得到解。

5|3code

#include <bits/stdc++.h> #define int long long using namespace std; const int mod = 5999993; int gethash(string s) { int ret = 0; for (int i = 0; i < s.length(); i++) { char ch = s[i]; ret = (ret * 29 + (ch - 'a' + 1)) % mod; } return ret; } void unhash(int x) { char res[1003]; int cnt = 0; while (x > 0) { res[cnt++] = (char)(x % 29 + 'a' - 1); x /= 29; } for (int i = cnt - 1; i >= 0; i--) cout << res[i]; } bool check(int x) { int cnt = 0; while (x > 0) { char ch = x % 29 + 'a' - 1; if (ch < 'a' || ch > 'z') return false; cnt++, x /= 29; } return cnt == 6; } int q_pow(int a, int b) { int ans = 1; while (b) { if (b & 1) ans = (ans * a) % mod; a = (a * a) % mod; b >>= 1; } return ans; } signed main() { int t; cin >> t; while (t--) { string s; cin >> s; int A = gethash(s); bool flag = false; int B = A; while(true) { B = (B * (int) q_pow(29, 6)) % mod; for (int i = 0; i <= 100; i++) { int B1 = A + mod * i - B; if (check(B1)) { cout << s; unhash(B1); cout << '\n'; flag = true; break; } } if (flag) break; } } return 0; }

6|0H

6|1题目描述

给你一个长度为 n 的数列 ai 。让你将这个数列划分成两个部分,我们称之为 presuf

pre 的长度为 Asuf 的长度为 B 。然后让所有 suf 中的数对 pre 中的数取模数,如果所有的数都相同,那么就可以这样划分。

问你 A 的最大值是多少。

6|2思路

首先对数列 ai 进行排序。可以发现:

1.假设所有兔子的最小值为:x ,将所有不为 x 的兔子全部变为绿色。因为小的值对大的值求余,结果一定是小的值。所以所有 x 对大于 x 的值求余,结果都是 x 、满足题意。即假设最小值出现次数为 cnt,则答案为 ncnt
2.若最大值为 x,且 x 对所有非 x 的数字求余结果相同。那么可以将所有非 x 的兔子涂成绿色。例如 2 2 2 3 4 13 13,可以将 2 2 2 3 4 全部涂绿,两个 13 对所有其他数字求余得到的结果都是 1 .即假设最大值的出现次数为 cnt,则答案为 ncnt
3.若最大值对其它数字求余结果均为 0 ,答案为 n-1 。例如 2 2 3 3 6 6 12 12。可以只剩个 12 为白兔子,其它的兔子全部涂成绿色.
4.先将所有数字排序,以某一数字 x 为分割点,所有小于等于 x 的兔子涂绿,其余兔子保持白色.这种情况最难判断,首先我们需要证明除此之外没有其它情况(即一定是以某一个数字作为分割点,白色兔子和绿色兔子分为两堆)

然后求前缀的 lcm ,其中 Qi 表示钱 i 个数字的最小公倍数,然后再求后缀差值的 gcdPi 表示 i n 这些数字差值的 gcd

然后枚举 i,若 Pi+1%Qi=0 则说明前 i 只兔子涂色是一种可行的方案。

6|3code

#include <bits/stdc++.h> #define N 100010 #define M 1000010 #define int long long using namespace std; int n, a[N], num[M]; int p[N], q[N]; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } int gcd(int a, int b) { if (!b) return a; else return gcd(b, a % b); } int lcm(int a, int b) { return a * b / gcd(a, b); } signed main() { n = read(); for (int i = 1; i <= n; i++) { a[i] = read(); num[a[i]]++; } if (num[a[1]] == n || num[a[1]] == 1) { cout << n - 1; return 0; } sort(a + 1, a + n + 1); int ans = 0; ans = max(n - num[a[1]], ans); q[1] = a[1]; int tot = 2; for (int i = 2; i <= n; i++) { q[i] = lcm(q[i - 1], a[i]); if (q[i] <= a[n]) tot++; } for (int i = n - 1; i >= 1; i--) { p[i] = gcd(p[i + 1], a[i + 1] - a[i]); } p[n] = a[n]; for (int i = 1; i <= n - 1 && i <= tot - 1; i++) if (p[i + 1] % q[i] == 0) ans = max(ans, i); cout << ans << "\n"; }

7|0F

7|1题目描述

一个人要打 CF ,现在给出所有比赛的开始时间和这个人要打当前这场的 rating 加减情况,分别用 aidi 表示开始时间和 rating 改变。每打一场比赛需要 k 的时间缓缓,这段时间内不能打任何比赛,如果打了第 i 场比赛,可以在 ai+k+1 的时候打下一场。

这个人打比赛的方式是,在一个时刻如果没有在休息,如果有比赛打,那就会立即打这场比赛,也可以在当前这个时刻决定放弃,那么剩下的比赛就一场也不打。

这个人至多可以打 m 场,也可以不打满 m 场。

可以选择任意时刻开始打比赛,问你上述条件下,打比赛的最大价值是多少。

7|2思路

比较裸地倍增维护。

维护三个数组 fathi,j fi,j gi,j

fathi,j 表示从第 i 个位置钦定他打 2j 场比赛之后紧接着可以打的比赛是哪一场。

fi,j 表示从第 i 个位置开始钦定他打 2j 场比赛的价值是多少。

gi,j 表示从第 i 个位置开始最多打满 2j 场比赛的最大价值是多少。

一开始算 fathi,0 的时候可以枚举每个位置,然后二分查找第一个大于等于 ai+k+1 的位置是哪里,也可以 lower_bound 求。

fi,0=di

gi,0=max{0,di}

可以得到如下的状态转移。

fathi,j=fathfathi,j1,j1

fi,j=fi,j1+ffathi,j1,j1

gi,j=max{gi,j1,fi,j1+gfathi,j1,j1}

然后枚举每个位置,求每个位置至多打 m 场比赛的最大价值。

求位置的最大价值的时候,可以倍增求解,注意每次加入一个区间的时候,在第 x 个位置处的答案:

ansx=max{sum+gx+t,j}

其中 sum 求的是从 x 枚举到此处的 fx,i 之和,好像还不太好解释,具体看代码。

7|3code

#include <bits/stdc++.h> #define N 200010 #define M 20 #define int long long using namespace std; int n, m, k, two[M]; int nex[N], f[N][M], g[N][M], fath[N][M]; struct node { int a, d; }thi[N]; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } signed main() { two[0] = 1; for (int i = 1; i <= 18; i++) two[i] = two[i - 1] * 2; n = read(), m = read(), k = read(); for (int i = 1; i <= n; i++) thi[i].a = read(); for (int i = 1; i <= n; i++) thi[i].d = read(); thi[n + 1].a = 1e9 + 1, thi[n + 2].a = 1e9 + 2; for (int i = 1; i <= n; i++) { int l = 1, r = n + 2; while (l < r) { // cout << l << " " << r << "\n"; int mid = (l + r) >> 1; if (thi[mid].a >= thi[i].a + k + 1) r = mid; else l = mid + 1; } if (l <= n) fath[i][0] = l; else fath[i][0] = 0; } for (int i = 1; i <= n; i++) { f[i][0] = thi[i].d; g[i][0] = max(0ll, thi[i].d); } for (int j = 1; j <= 18; j++) for (int i = 1; i <= n; i++) { f[i][j] = f[i][j - 1] + f[fath[i][j - 1]][j - 1]; fath[i][j] = fath[fath[i][j - 1]][j - 1]; g[i][j] = max(g[i][j - 1], f[i][j - 1] + g[fath[i][j - 1]][j - 1]); } int ans = 0; for (int i = 1; i <= n; i++) { int cnt = 0, x = i, flag = 0, sum = 0, tes = m; for (int j = 18; j >= 0; j--) if (two[j] <= tes) { tes -= two[j]; if (!flag) { flag = 1; cnt = max(cnt, g[x][j]); } else cnt = max(cnt, sum + g[x][j]); sum += f[x][j]; x = fath[x][j]; ans = max(ans, cnt); if (x == 0) break; } } cout << ans << "\n"; return 0; }

__EOF__

本文作者Kersen
本文链接https://www.cnblogs.com/zzz-hhh/p/18139524.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Kersen  阅读(31)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示