当前位置: 首页 > news >正文

2025年5月月赛 乙组T1~T3

目录

T1 逆序对数

T2 平衡 01 串

T3 城市漫步

总结


T1 逆序对数

上海市计算机学会竞赛平台 | YACS

p为n的排列,没有重复元素。

p的子序列要和p的逆序对数相等,则该子序列要包含p的所有逆序对的数字,其他数字可包含可不包含。

设k = 不是逆序对的数字个数,则符合要求的p的子序列有(2^k) mod Mo 个,若k = n,则符合要求的p的子序列有(2^k - 1) mod Mo个(不包含空子序列)。

对p归并排序,在归并排序的过程中标记逆序对。

代码如下:

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;typedef long long LL;const LL Maxn = 1e5 + 5;
const LL Mo = 998244353;bool vis[Maxn];void f_qsort(LL left, LL mid, LL right, vector<LL>& vct) {vector<LL> v_L(vct.begin() + left, vct.begin() + mid + 1);vector<LL> v_R(vct.begin() + mid + 1, vct.begin() + right + 1);bool flag = false;for (LL k = left, i = 0, j = 0; k <= right; ++k) {if (j >= v_R.size() && i < v_L.size()) {vct[k] = v_L[i++];} else if (i >= v_L.size() && j < v_R.size()) {vct[k] = v_R[j++];} else if (v_L[i] > v_R[j]) {vis[v_R[j]] = true;if (flag == false) {for (LL s = i; s < v_L.size(); ++s) {vis[v_L[s]] = true;flag = true;}}vct[k] = v_R[j++];} else if (v_L[i] < v_R[j]) {vct[k] = v_L[i++];}}
}void f_zsort(LL left, LL right, vector<LL>& vct) {if (left >= right)  return;LL mid = left + ((right - left) >> 1);f_zsort(left, mid, vct);f_zsort(mid + 1, right, vct);f_qsort(left, mid, right, vct);
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);LL t, n, cnt = 0, res = 1;cin >> t;while (t--) {cin >> n;vector<LL> vct(n, 0);for (LL i = 0; i < n; ++i) {cin >> vct[i];vis[vct[i]] = false;}f_zsort(0, n - 1, vct);cnt = 0;res = 1;for (LL i = 0; i < n; ++i) {if (vis[vct[i]] == false) {res = (res * 2) % Mo;++cnt;}}if (cnt < n)  cout << res << '\n';else  cout << res - 1 << '\n';}return 0;
}

在f_qsort函数的for循环里重复标记会超时。

前面的遍历只要发现了逆序对,设前面遍历到v_L的i位置,v_R的j位置,则[v_L[i], v_L[v_L.size() - 1]]和v_R[j]被标记,当前发现逆序对设当前遍历到v_L的i’位置,v_R的j’位置,则[v_L[i’], v_L[v_L.size() - 1]]和v_R[j’]被标记,i < i'重复标记了,所以只在第一次发现逆序对时标记v_L。原理如下图:

当v_R[j] < v_L[i]时出现逆序对,因为v_L数组有序,则i之后的元素也和v_R[j]组成逆序对。

T2 平衡 01 串

上海市计算机学会竞赛平台 | YACS

答案具有单调性且能在O(n)时间内判定,用二分答案。

最优解:最小权重

判定函数:

  • 思路 贪心,区间[start + 1, last - 1]外的1个数 = mid,不断调整start、last看区间[start + 1, last - 1]是否有[start + 1, last - 1]内的0 <= mid个的情况,若有返回true,尝试所有依旧不存在0 <= mid返回false。
  • 实现 双指针法,开始start移至[0, start]1的个数 = mid,last移到[last, str.size() - 1]全为0,,判断权值是否 <= mid,之后循环mid次,每次start移到前面一个为1的位置([start + 1, last - 1]包含进1个1),last移到前一个为1的位置之后([start + 1, last - 1]退出1个1),然后判断权值是否 <= mid。该函数时间复杂度约为O(n)。

代码如下:

#include <iostream>
#include <string>
#include <cmath>
using namespace std;typedef long long LL;const LL Maxn = 2 * 1e5 + 5;LL vct[Maxn];void init(LL n) {for (LL i = 0; i <= n; ++i)  vct[i] = 0;
}bool f_check(LL mid, LL len, string& str) {LL start = 0, last = len - 1, cnt = 0;while (start < len) {if (str[start] == '1') {if (cnt >= mid)  break;++cnt;++start;} else {++start;}}--start;while (last >= 0 && str[last] == '0')  --last;++last;if (start > last - 1)  return true;if (start == -1 && vct[last - 1] <= mid)  return true;if (start != -1 && vct[last - 1] - vct[start] <= mid)  return true;for (LL s = 1; s <= cnt; ++s) {while (start >= 0 && str[start] == '0')  --start;--start;last -= 2;while (last >= 0 && str[last] == '0')  --last;++last;if (start > last - 1)  return true;if (start == -1 && vct[last - 1] <= mid)  return true;if (start != -1 && vct[last - 1] - vct[start] <= mid)  return true;}return false;
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);LL t, scnt1 = 0, L = 0, R = 0, mid = 0;string str;cin >> t;while (t--) {cin >> str;init(str.size());scnt1 = 0;for (LL i = 0; i < str.size(); ++i) {if (i > 0)  vct[i] = vct[i - 1];if (str[i] == '0')  ++vct[i];else  ++scnt1;}L = 0, R = scnt1;while (L < R) {mid = ((L + R) >> 1);if (f_check(mid, str.size(), str) != false)  R = mid;else  L = mid + 1;}cout << L << '\n';}return 0;
}

改变区间左右端点可用双指针,暴力两层for循环枚举区间也可看作双指针。注意指针初始化和while循环结束后的指针状态及指针的偏移量。

T3 城市漫步

上海市计算机学会竞赛平台 | YACS

树(图是树加上若干环和重边)由点和边组成,这道题考虑点比较复杂,那么考虑边。树上两点间只有一条路径,该路径是简单路径。

先假设y = x,贪心,只考虑必须经过的边(到达ai路径上的边)这条边是不能省的,遇到这种边sum += 2(包括回去路径)。

考虑y,两种情况,设c,d间的路径长度为dis(c, d)

  • y在到ai的路径上,sum -= dis(x, y),不用再回x
  • y不在到ai的路径上,贪心,sum加上的尽可能少。回到x后再走到y是sum += dis(x, y),设能到达y(y的父节点)且是经过点的为t,不再从t走到x,从t直接到y,sum = sum - dis(x, t) + dis(t, y),可看出t离y越近sum减的越多,dis(t,y) < dis(x, y),sum = sum - dis(x, t) + dis(t, y)方案最优。

第一种情况是第二种情况的特殊情况。

分析可得,需求每个节点到根节点的距离、y的父节点到y的距离,是x到ai的路径上的点要标记。

代码如下:

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;typedef long long LL;const LL Maxk = 2 * 1e5 + 5;
const LL Maxn = 2 * 1e5 + 5;
const LL MVal = 1e14;vector<LL> grid[Maxn];
LL Dis[Maxn], visy[Maxn], idx = 0;
bool visk[Maxn];void init(LL n) {for (LL i = 0; i <= n; ++i) {visk[i] = false;visy[i] = MVal;Dis[i] = 0;grid[i].clear();}
}LL dfs(LL u, LL fa) {LL sum = 0;for (auto v : grid[u]) {if (v == fa)  continue;Dis[v] = Dis[u] + 1;sum += dfs(v, u);}if (sum > 0)  visk[u] = true;if (visy[u] < MVal) {if (visk[u] != false && idx == 0)  idx = u;visy[fa] = visy[u] + 1;}if (sum > 0 || visk[u] != false)  return sum + 2;else  return 0;
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);LL t, n, k, x, y, u, v, sum = 0;cin >> t;while (t--) {cin >> n >> k >> x >> y;init(n);visy[y] = 0;visk[x] = true;for (LL i = 1; i <= k; ++i) {cin >> u;visk[u] = true;}for (LL i = 1; i < n; ++i) {cin >> u >> v;grid[u].push_back(v);grid[v].push_back(u);}idx = 0;sum = dfs(x, 0) - 2;cout << (sum + visy[idx] - Dis[idx]) << '\n';}return 0;
}

一遍dfs即可,对于无根树,dfs函数的参数直接明确父子节点,直接求visy即可。

能一次dfs的不要多次dfs,递归费栈空间。

总结

1.先想暴力法,再优化

  • 可二分答案或查找且能O(n)时间内判定,用二分
  • 要取最大优先级元素,用堆(priority_queue)优化
  • 区间合并、查找等用线段树优化
  • dfs有递推关系的,用动规,只要有阶段、状态、状态转移就用动规
  • 枚举区间端点看是否能用双指针,双指针可看做多个变量同时工作,可提高效率。

2.对于图的问题点难考虑,尝试用边考虑。

3.看到排列、元素不重复、限制条件等信息不要忽略,可能是突破口,求方案数的题不一定要硬算方案数

4.逻辑写清楚比少写几行代码更重要

相关文章:

  • 建筑设备一体化监控系统:提升能效与运维效率
  • Kubernetes 集群到 Jumpserver
  • 软件开发中的“需求镀金”现象如何避免?
  • web第十次课后作业--Mybatis的增删改查
  • 中英文翻译数据集(17245条),AI智能体知识库数据收集~
  • COMSOL学习笔记-静电场仿真
  • 如何防止看板任务长期停滞不前
  • ROS2--导航仿真
  • 日语学习-日语知识点小记-构建基础-JLPT-N4阶段(32):そうやすいにくいすぎ(過ぎ)
  • Java-IO流之缓冲流详解
  • 桌面鼠标右键新建没有记事本怎么恢复
  • 【缺陷】温度对半导体缺陷电荷态跃迁能级的影响
  • Spring AI(11)——SSE传输的MCP服务端
  • 智慧供水运维管理系统
  • RMSE可以融合均值与标准差
  • DFORMER: RETHINKING RGBD REPRESENTATION LEARNING FOR SEMANTIC SEGMENTATION 论文浅析
  • [科研理论]机器人路径规划算法总结及fast_planner经典算法解读
  • 管易云OMS系统对接流程
  • 残月个人拟态主页
  • 如何做好一份技术文档?(下篇)
  • 内容网站管理系统/宁波seo排名公司
  • wordpress 文章的id/北京做网络优化的公司
  • 山东手工活外发加工网/东莞市网络seo推广服务机构
  • 家庭宽带做网站服务器/网站seo应用
  • 布吉做棋牌网站建设有哪些公司/免费推广网站大全下载安装
  • 网页设计师的能力/宁波seo服务推广