CCF-CSP认证考试 202303-4 星际网络II 100分题解

更多 CSP 认证考试题目题解可以前往:CSP-CCF 认证考试真题题解


原题链接: 202303-4 星际网络II

时间限制: 2.0s
内存限制: 1.0GB

问题描述

随着星际网络的进一步建设和规模的增大,一个新的问题出现在网络工程师面前——地址空间不够用了!原来,星际网络采用了传统的IPv6协议,虽然有 2 128 2^{128} 2128 级别的可用地址数量,但面对广袤无垠的宇宙和爆炸式增长的网络用户数,如此庞大的地址空间也面临了用尽的那一天。

新的通信协议的研发工作交给了著名的网络科技圣地——西西艾弗星。最终,经过2333年的不懈努力,西西艾弗星的工程师们设计出了一种新的协议——“西西艾弗IP协议”,又称IPxxaf。

在IPxxaf协议中,一个地址由 n n n 位二进制位组成,其中 n n n 16 16 16 的倍数。日常表示一个地址时,采用类似IPv6协议的十六进制表示法,每 4 4 4 位用 : 隔开。如 n = 32 n=32 n=32 时,地址为 2a00:0001 ,即表示一个二进制为 0010 1010 0000 0000 0000 0000 0000 0001 的地址。注意不会出现IPv6中省略每组的前导 0 或用 :: 省略一段 0 的情况。

为方便起见,记 n u m ( s ) num(s) num(s) 为地址 s 按高位在前、低位在后组成的 n n n 位二进制数,称一段“连续的地址“为 n u m ( s ) num(s) num(s) 成一段连续区间的一系列地址。

西西艾弗星的网络管理员负责地址的分配与管理。最开始,整个地址空间都是未分配的。用户可以随时向管理员申请一些地址:

1 id l r:表示用户 i d id id 申请地址在 l ∼ r l\sim r lr 范围内(包含 l l l r r r,下同)的一段连续地址块。

在地址申请操作中,管理员需要先检查地址是否可用。如果用户申请的地址全部未被分配,则检查通过;若地址中存在已经分配给其他用户的地址,则检查失败。

但有一种特殊情况:申请的地址中没有已经分配给其他用户的地址,但含有一些先前已分配给该用户本人的地址。此时可以认为检查通过,但若申请的地址先前已全部分配给该用户则检查失败。

如果上述检查通过,则管理员向用户返回 YES,并将申请的地址分配给该用户;若不通过,则向用户返回 NO,同时不改变现有的地址分配。

网络管理员要定期检查地址的分配情况,具体而言有如下两种操作:

2 s:检查地址 s s s 被分配给了哪个用户。若未被分配,则结果为 0 0 0

3 l r:检查 l ∼ r l\sim r lr 范围内的所有地址是否完整地分配给了某个用户。若是,回答该用户的编号;若否,回答 0 0 0

在整个网络的运行过程中,共出现了 q q q 次申请地址和检查地址分配的操作。作为西西艾弗星的一名重要的网络技术顾问,你要帮网络管理员依次处理每个操作,并回答相应的结果。

输入格式

从标准输入读入数据。

第一行, 2 2 2 个正整数 n , q n,q n,q

接下来 q q q 行,每行一个操作,格式如上所述,其中的 i d id id 为正整数, l , r , s l,r,s l,r,s 均为IPxxaf地址串,其中十六进制均用数字和小写字母表示。

输出格式

输出到标准输出。

输出 q q q 行,每行一个非负整数或字符串,表示此次操作的结果。

其中,对于操作 1 1 1 ,输出 YES 或 NO;对于操作 2 , 3 2,3 2,3,输出一个非负整数。

样例输入1

32 12
1 1 0001:8000 0001:ffff
2 0001:a000
3 0001:c000 0001:ffff
1 2 0000:0000 000f:ffff
2 0000:1000
1 1 0001:8000 0001:8fff
1 2 0000:0000 0000:ffff
2 0000:1000
1 1 0002:8000 0002:ffff
3 0001:8000 0002:ffff
1 1 0001:c000 0003:ffff
3 0001:8000 0002:ffff

样例输出1

YES
1
1
NO
0
NO
YES
2
YES
0
YES
1

样例解释

4 4 4 个操作时,由于用户 2 2 2 申请的部分地址已被分配给用户 1 1 1,因此申请不通过;

6 6 6 个操作时,由于用户 1 1 1 申请的全部地址已被分配给用户 1 1 1,因此申请不通过;

11 11 11 个操作时,用户 1 1 1 申请的部分地址已被分配给用户 1 1 1,其余地址尚未被分配,申请通过;

数据范围

对于所有数据, n ≤ 512 , q ≤ 5 × 1 0 4 n \leq 512,\ q\leq 5\times 10^4 n512, q5×104 n n n 16 16 16 的倍数, i d ≤ q id \leq q idq,对于操作 1 , 3 1,3 1,3 保证 n u m ( l ) ≤ n u m ( r ) num(l) \leq num(r) num(l)num(r)

测试点编号 n ≤ n\le n q ≤ q \le q特殊性质
1 ∼ 4 1\sim 4 14 16 16 16 200 200 200
5 ∼ 6 5\sim 6 56 64 64 64 200 200 200
7 ∼ 9 7\sim 9 79 512 512 512 200 200 200
10 ∼ 11 10\sim 11 1011 16 16 16 20000 20000 20000
12 ∼ 13 12\sim 13 1213 64 64 64 50000 50000 50000
14 ∼ 16 14\sim 16 1416 512 512 512 50000 50000 50000所有操作 1 的 i d id id 互不相同
17 ∼ 20 17\sim 20 1720 512 512 512 50000 50000 50000

题解

设计到的地址最多有 2 512 2^{512} 2512 个,肯定存不下,将所有涉及到的地址进行离散化。

将所有可能的数放到一个 vector 中,然后运用下列代码将其离散化:

sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());

离散化后,查询 i d id id 的离散化后的值即可使用:

lower_bound(v.begin(), v.end(), id) - v.begin();

使用线段树维护支持区间赋值和区间查询( x x x 的单点查询可看成 [ x , x ] [x,x] [x,x] 的区间查询)的计数值 c n t cnt cnt(有多少个地址被分配了)、最大值 m x mx mx(未分配的地址视为 − ∞ -\infty )、最小值 m n mn mn(未分配的地址视为 + ∞ +\infty +)。

对于 1 id l r 操作(操作中的 l , r l,r l,r 均要转换成离散后的值,下同):

  • 查询区间 [ l , r ] [l,r] [l,r] c n t , m x , m n cnt,mx,mn cnt,mx,mn
  • 全部未分配,即 c n t = 0 cnt=0 cnt=0,此时操作成功;
  • 余下情况是已经被分配过了(此时 m x , m n mx,mn mx,mn 均不为 ∞ \infty ),此时判断是不是仅分配给该用户,即 m x = m n = i d mx=mn=id mx=mn=id,否则操作失败;
  • 余下情况是已经被分配过了,且仅分配给了该用户,此时判断是否全部分配给了该用户,即 c n t = r − l + 1 cnt=r-l+1 cnt=rl+1,满足则操作失败,否则操作成功;
  • 操作成功后给区间 [ l , r ] [l,r] [l,r] 赋值,对于 c n t cnt cnt 来说,区间的值均为 1 1 1;对于 m x , m n mx,mn mx,mn 来说,区间的值均为 i d id id
  • 为了防止原先不连续的区间变成了连续的(区间 [ 0 , 1 ] , [ 3 , 4 ] [0,1],[3,4] [0,1],[3,4] 离散化后有可能变为 [ 0 , 1 ] , [ 2 , 3 ] [0,1],[2,3] [0,1],[2,3]),要将右端点 + 1 +1 +1 的值一并进行离散化。

对于 2 s 操作:仅需要判断区间 [ s , s ] [s,s] [s,s] c n t cnt cnt 值是否为 1 1 1 即可知是否分配给了用户,如果分配给了用户,输出 m x mx mx 或者 m n mn mn 均可,否则输出 0 0 0

对于 3 l r 操作:先判断是否全部进行了分配,即区间 [ l , r ] [l,r] [l,r] c n t cnt cnt 是否满足 c n t = r − l + 1 cnt=r-l+1 cnt=rl+1,然后再判断是否分配给了同一个用户,即 m x = m n mx=mn mx=mn,如果满足输出 m x mx mx 或者 m n mn mn 均可,否则输出 0 0 0

时间复杂度: O ( n q log ⁡ q ) \mathcal{O}(nq\log q) O(nqlogq)

参考代码(296ms,54.10MB)

/*Created by Pujx on 2024/3/21.
*/
#pragma GCC optimize(2, 3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define int long long
//#define double long double
using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
#define inf (int)0x3f3f3f3f3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define yn(x) cout << (x ? "yes" : "no") << endl
#define Yn(x) cout << (x ? "Yes" : "No") << endl
#define YN(x) cout << (x ? "YES" : "NO") << endl
#define mem(x, i) memset(x, i, sizeof(x))
#define cinarr(a, n) for (int i = 1; i <= n; i++) cin >> a[i]
#define cinstl(a) for (auto& x : a) cin >> x;
#define coutarr(a, n) for (int i = 1; i <= n; i++) cout << a[i] << " \n"[i == n]
#define coutstl(a) for (const auto& x : a) cout << x << ' '; cout << endl
#define all(x) (x).begin(), (x).end()
#define md(x) (((x) % mod + mod) % mod)
#define ls (s << 1)
#define rs (s << 1 | 1)
#define ft first
#define se second
#define pii pair<int, int>
#ifdef DEBUG#include "debug.h"
#else#define dbg(...) void(0)
#endifconst int N = 2e5 + 5;
//const int M = 1e5 + 5;
const int mod = 998244353;
//const int mod = 1e9 + 7;
//template <typename T> T ksm(T a, i64 b) { T ans = 1; for (; b; a = 1ll * a * a, b >>= 1) if (b & 1) ans = 1ll * ans * a; return ans; }
//template <typename T> T ksm(T a, i64 b, T m = mod) { T ans = 1; for (; b; a = 1ll * a * a % m, b >>= 1) if (b & 1) ans = 1ll * ans * a % m; return ans; }int a[N];
int n, m, t, k, q;vector<vector<int>> v;
struct query { int op, id; vector<int> l, r; } qu[N];template <typename T> struct SegmentTree {struct TreeNode { int l, r; T st, cnt, mx, mn; } tr[N << 2];void pushup(int s) {tr[s].cnt = tr[ls].cnt + tr[rs].cnt;tr[s].mx = max(tr[ls].mx, tr[rs].mx);tr[s].mn = min(tr[ls].mn, tr[rs].mn);}void pushdown(int s) {if (tr[s].st != numeric_limits<T>::min()) {tr[ls].st = tr[rs].st = tr[s].st;tr[ls].cnt = tr[ls].r - tr[ls].l + 1;tr[rs].cnt = tr[rs].r - tr[rs].l + 1;tr[ls].mx = tr[rs].mx = tr[s].st;tr[ls].mn = tr[rs].mn = tr[s].st;tr[s].st = numeric_limits<T>::min();}}void build(int l, int r, int s = 1) {tr[s].l = l, tr[s].r = r;tr[s].st = numeric_limits<T>::min();tr[s].cnt = 0;tr[s].mx = numeric_limits<T>::min();tr[s].mn = numeric_limits<T>::max();if (l == r) return;int mid = l + r >> 1;if (l <= mid) build(l, mid, ls);if (mid < r) build(mid + 1, r, rs);pushup(s);}void modify(int l, int r, T val, int s = 1) {if (l <= tr[s].l && tr[s].r <= r) {tr[s].st = val;tr[s].cnt = tr[s].r - tr[s].l + 1;tr[s].mx = tr[s].mn = val;return;}pushdown(s);int mid = tr[s].l + tr[s].r >> 1;if (l <= mid) modify(l, r, val, ls);if (mid < r) modify(l, r, val, rs);pushup(s);}T query(int l, int r, int s = 1) {if (l <= tr[s].l && tr[s].r <= r) return tr[s].cnt;int mid = tr[s].l + tr[s].r >> 1;T ans = T();pushdown(s);if (l <= mid) ans += query(l, r, ls);if (mid < r) ans += query(l, r, rs);return ans;}T queryMax(int l, int r, int s = 1) {if (l <= tr[s].l && tr[s].r <= r) return tr[s].mx;int mid = tr[s].l + tr[s].r >> 1;T ans = numeric_limits<T>::min();pushdown(s);if (l <= mid) ans = max(ans, queryMax(l, r, ls));if (mid < r) ans = max(ans, queryMax(l, r, rs));return ans;}T queryMin(int l, int r, int s = 1) {if (l <= tr[s].l && tr[s].r <= r) return tr[s].mn;int mid = tr[s].l + tr[s].r >> 1;T ans = numeric_limits<T>::max();pushdown(s);if (l <= mid) ans = min(ans, queryMin(l, r, ls));if (mid < r) ans = min(ans, queryMin(l, r, rs));return ans;}
};
SegmentTree<int> T;vector<int> read() {string s; cin >> s;vector<int> ans(n / 16, 0);for (int i = 0; i < s.length(); i += 5)for (int j = 0; j < 4; j++)ans[i / 5] = ans[i / 5] * 16 + s[i + j] - (s[i + j] < 'a' ? '0' : 'a' - 10);v.emplace_back(ans);return ans;
}void work() {cin >> n >> q;v.emplace_back(vector<int>(n / 16, -1));for (int i = 1; i <= q; i++) {cin >> qu[i].op;if (qu[i].op == 1) {cin >> qu[i].id, qu[i].l = read(), qu[i].r = read();if (qu[i].r != vector<int>(n / 16, 65535)) {vector<int> tem = qu[i].r;int p = tem.size() - 1;while (tem[p] == 65535) tem[p--] = 0;tem[p]++;v.emplace_back(tem);}}else if (qu[i].op == 2) qu[i].l = qu[i].r = read();else qu[i].l = read(), qu[i].r = read();}sort(v.begin(), v.end());v.erase(unique(v.begin(), v.end()), v.end());T.build(1, v.size() - 1);for (int i = 1; i <= q; i++) {int l = lower_bound(all(v), qu[i].l) - v.begin();int r = lower_bound(all(v), qu[i].r) - v.begin();int cnt = T.query(l, r), mx = T.queryMax(l, r), mn = T.queryMin(l, r);if (qu[i].op == 1) {bool flag = !cnt || (mx == mn && mx == qu[i].id && cnt < r - l + 1);if (flag) T.modify(l, r, qu[i].id);YN(flag);}else if (qu[i].op == 2) cout << (cnt ? mx : 0) << endl;else cout << (cnt == r - l + 1 && mx == mn ? mx : 0) << endl;}
}signed main() {
#ifdef LOCALfreopen("C:\\Users\\admin\\CLionProjects\\Practice\\data.in", "r", stdin);freopen("C:\\Users\\admin\\CLionProjects\\Practice\\data.out", "w", stdout);
#endifios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int Case = 1;//cin >> Case;while (Case--) work();return 0;
}
/*_____   _   _       _  __    __|  _  \ | | | |     | | \ \  / /| |_| | | | | |     | |  \ \/ /|  ___/ | | | |  _  | |   }  {| |     | |_| | | |_| |  / /\ \|_|     \_____/ \_____/ /_/  \_\
*/

关于代码的亿点点说明:

  1. 代码的主体部分位于 void work() 函数中,另外会有部分变量申明、结构体定义、函数定义在上方。
  2. #pragma ... 是用来开启 O2、O3 等优化加快代码速度。
  3. 中间一大堆 #define ... 是我习惯上的一些宏定义,用来加快代码编写的速度。
  4. "debug.h" 头文件是我用于调试输出的代码,没有这个头文件也可以正常运行(前提是没定义 DEBUG 宏),在程序中如果看到 dbg(...) 是我中途调试的输出的语句,可能没删干净,但是没有提交上去没有任何影响。
  5. ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); 这三句话是用于解除流同步,加快输入 cin 输出 cout 速度(这个输入输出流的速度很慢)。在小数据量无所谓,但是在比较大的读入时建议加这句话,避免读入输出超时。如果记不下来可以换用 scanfprintf,但使用了这句话后,cinscanfcoutprintf 不能混用。
  6. main 函数和 work 函数分开写纯属个人习惯,主要是为了多组数据。

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

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

相关文章

uniapp ios证书失效

前面是按照网上查找的方法 作者大大的地址 1、一个ios账户&#xff08;688付费版&#xff09; 2、登录 Apple Developer 3、创建Identifiers ps&#xff1a;创建时需继承苹果的sdk&#xff0c;只需要一个就行 点击continue再点击Register即可 4、创建.cer证书 &…

tcp seq ack

seq&#xff08;Sequence Number&#xff09;&#xff1a;32bits&#xff0c;表示这个tcp包的序列号。tcp协议拼凑接收到的数据包时&#xff0c;根据seq来确定顺序&#xff0c;并且能够确定是否有数据包丢失。 ack&#xff08;Acknowledgment Number&#xff09;&#xff1a;3…

安卓开发日记:实现APP重启逻辑,适用于热更后重启游戏进行加载

可根据合适的弹窗搭配使用重启逻辑&#xff0c;建议使用在热更包加载后使用&#xff0c;帮助部分热更后未及时生效的逻辑范围首先&#xff0c;在逻辑调用Activity类中创建一个成员变量&#xff0c;给后续逻辑接口直接使用 如下 public class MainActivity extends Activity {…

想当初级爬虫工程师,需要把爬虫学到什么程度?

这篇文章会说说我自己的心得体验&#xff0c;关于爬虫、关于工作&#xff0c;仅供参考。 学到哪种程度 暂且把目标定位初级爬虫工程师&#xff0c;简单列一下吧&#xff1a; &#xff08;必要部分&#xff09; 语言选择&#xff1a;一般是了解Python、Java、Golang之一 熟悉…

SCI一区 | Matlab实现SSA-TCN-BiGRU-Attention麻雀算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测

SCI一区 | Matlab实现SSA-TCN-BiGRU-Attention麻雀算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测 目录 SCI一区 | Matlab实现SSA-TCN-BiGRU-Attention麻雀算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测预测效果基本介绍模型描述程序…

【回溯、分治、Kadane】算法例题

目录 十五、回溯 101. 电话号码的字母组合 ② 102. 组合 ② 103. 全排列 ② 104. 组合总和 ② 105. N皇后II ③ 106. 括号生成 ② 107. 单词搜索 ② 十六、分治 108. 将有序数组转换为二叉搜索树 ① 109. 排序链表 ② 110. 建立四叉树 ② 111. 合并K个升序链表 ③ …

Vue2在一个页面内动态切换菜单显示对应的路由组件

项目的需求是在一个页面内动态获取导航菜单&#xff0c;导航菜单切换的时候显示对应的路由页面&#xff0c;类似于tab切换的形式&#xff0c;切换的导航菜单和页面左侧导航菜单是同一个路由组件&#xff0c;只是放到了一个页面上&#xff0c;显示的个数不同&#xff0c;所有是动…

基于Python的Django开发接口框架搭建

1、安装Django&#xff0c;如图1 命令行执行&#xff1a;pip install django 2、创建Django项目&#xff0c;如图2 django-admin startproject imooc 3、创建Django的应用项目&#xff0c;如图3 必须进入项目文件下执行命令&#xff1a; python3 manage.py startapp app 4、…

Python基础学习笔记(一)

Python简介 Python 语言是一种跨平台、开源、免费、解释型、面向对象、动态数据类型的高级程序设计语言。早期版本的 Python 被称作是 Python1&#xff1b;Python2 最后一个版本是 2.7&#xff1b;Python3 是目前最活跃的版 本&#xff0c;基本上新开发的 Python 代码都会支持…

线程池实现“线程复用”的原理

线程池实现“线程复用”的原理 学习线程复用的原理&#xff0c;以及对线程池的 execute 这个非常重要的方法进行源码解析。 线程复用原理 我们知道线程池会使用固定数量或可变数量的线程来执行任务&#xff0c;但无论是固定数量或可变数量的线程&#xff0c;其线程数量都远远…

27-4 文件上传漏洞 - 黑名单绕过

环境准备:构建完善的安全渗透测试环境:推荐工具、资源和下载链接_渗透测试靶机下载-CSDN博客 一、黑名单绕过和黑白名单机制: 黑名单:黑名单中的文件不允许通过。白名单:白名单中的文件允许通过。二、黑白名单判断: 当输入一串后缀如"sfahkfhakj"时,黑名单不…

2024/03/21(网络编程·day7)

一、思维导图 二、 //定义删除函数 int do_delete(sqlite3 *ppDb) {int del_numb0;printf("请输入要删除的学生的学号:");scanf("%d",&del_numb);getchar();//准备sql语句char sql[128]"select *from Stu";sprintf(sql,"delete from …

贾志杰“大前端”系列著作出版发行

杰哥著作《VueSpringBoot前后端分离开发实战》2021年出版以来&#xff0c;累计发行2.6万册&#xff0c;受到广大读者热捧。后应读者要求&#xff0c;受出版社再次邀请&#xff0c;“大前端”系列之《剑指大前端全栈工程师》、《前端三剑客》由清华大学出版社陆续出版发行。系列…

奥特曼回应GPT5

欢迎再次与大家会面&#xff01;在积累了大量的信息和趋势后&#xff0c;今天我们将深入了解 Sora、OpenAI 董事会、以及近期与其有关的所有声讨。我们将直接跳入与 OpenAI 首席执行官 Sam Altman 的深度访谈&#xff0c;探讨从 AGI 到 GPT-5 的未来&#xff0c;以及 Sam 对人工…

敢为天下先!深圳市全力推动鸿蒙生态发展……程序员

3月19日&#xff0c;鸿蒙生态创新中心揭幕仪式在深圳正式举行。鸿蒙生态创新中心的建立是为构建先进完整、自主研发的鸿蒙生态体系&#xff0c;将深圳打造为鸿蒙生态策源地、集聚区的具体举措&#xff0c;也是推动我国关键核心技术高水平自立自强、数字经济高质量发展、保障国家…

有哪些工具可以替代Gitbook?这篇文章告诉你

你是否曾经在搜索在线文档创建和共享工具时&#xff0c;遇到了Gitbook? Gitbook 是一个相当出色的工具&#xff0c;具有强大的编辑和发布功能&#xff0c;但也有其不足之处&#xff0c;如使用起来有一定的技术要求&#xff0c;入门门槛较高等。如果你正在寻找Gitbook的替代品&…

ChatGPT为您的论文写作提供无限可能

ChatGPT无限次数:点击直达 在现代科技的快速发展下&#xff0c;人工智能正逐渐渗透到各个领域&#xff0c;为我们的生活和工作带来了诸多便利和创新。其中&#xff0c;ChatGPT作为开放AI的一项重要成果&#xff0c;以其卓越的自然语言处理能力&#xff0c;为我们的论文写作提供…

CSS问题精粹1

1.关于消除<li>列表前的符号 我相信很多人在初学CSS时会遇到该问题&#xff0c;无论是创作导航&#xff0c;还是列表&#xff0c;前面都会有个黑点点或其它符号。 解决该问题其实很简单 采用list-style-type:none或list-style:none直接解决 如果你想更换前面的黑点点&a…

java怎么做带进度条的上传

在Java中实现带进度条的文件上传功能通常涉及到前后端的配合工作。前端负责收集文件并展示上传进度&#xff0c;后端负责接收和处理文件&#xff0c;并提供进度信息给前端。 前端部分&#xff1a; HTML&#xff1a;创建文件输入控件和进度条元素。 <input type"file…

QT界面制作

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);this->setWindowFlag(Qt::FramelessWindowHint);//接收动图QMovie *mv new QMovie(":/pictrue/th.gif…