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

2024-03-22 22:36

本文主要是介绍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 函数分开写纯属个人习惯,主要是为了多组数据。

这篇关于CCF-CSP认证考试 202303-4 星际网络II 100分题解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/836346

相关文章

Linux中压缩、网络传输与系统监控工具的使用完整指南

《Linux中压缩、网络传输与系统监控工具的使用完整指南》在Linux系统管理中,压缩与传输工具是数据备份和远程协作的桥梁,而系统监控工具则是保障服务器稳定运行的眼睛,下面小编就来和大家详细介绍一下它... 目录引言一、压缩与解压:数据存储与传输的优化核心1. zip/unzip:通用压缩格式的便捷操作2.

基于Python实现一个简单的题库与在线考试系统

《基于Python实现一个简单的题库与在线考试系统》在当今信息化教育时代,在线学习与考试系统已成为教育技术领域的重要组成部分,本文就来介绍一下如何使用Python和PyQt5框架开发一个名为白泽题库系... 目录概述功能特点界面展示系统架构设计类结构图Excel题库填写格式模板题库题目填写格式表核心数据结构

Linux网络配置之网桥和虚拟网络的配置指南

《Linux网络配置之网桥和虚拟网络的配置指南》这篇文章主要为大家详细介绍了Linux中配置网桥和虚拟网络的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、网桥的配置在linux系统中配置一个新的网桥主要涉及以下几个步骤:1.为yum仓库做准备,安装组件epel-re

python如何下载网络文件到本地指定文件夹

《python如何下载网络文件到本地指定文件夹》这篇文章主要为大家详细介绍了python如何实现下载网络文件到本地指定文件夹,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下...  在python中下载文件到本地指定文件夹可以通过以下步骤实现,使用requests库处理HTTP请求,并结合o

Linux高并发场景下的网络参数调优实战指南

《Linux高并发场景下的网络参数调优实战指南》在高并发网络服务场景中,Linux内核的默认网络参数往往无法满足需求,导致性能瓶颈、连接超时甚至服务崩溃,本文基于真实案例分析,从参数解读、问题诊断到优... 目录一、问题背景:当并发连接遇上性能瓶颈1.1 案例环境1.2 初始参数分析二、深度诊断:连接状态与

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

Spring Security自定义身份认证的实现方法

《SpringSecurity自定义身份认证的实现方法》:本文主要介绍SpringSecurity自定义身份认证的实现方法,下面对SpringSecurity的这三种自定义身份认证进行详细讲解,... 目录1.内存身份认证(1)创建配置类(2)验证内存身份认证2.JDBC身份认证(1)数据准备 (2)配置依

SpringSecurity JWT基于令牌的无状态认证实现

《SpringSecurityJWT基于令牌的无状态认证实现》SpringSecurity中实现基于JWT的无状态认证是一种常见的做法,本文就来介绍一下SpringSecurityJWT基于令牌的无... 目录引言一、JWT基本原理与结构二、Spring Security JWT依赖配置三、JWT令牌生成与

SpringSecurity6.0 如何通过JWTtoken进行认证授权

《SpringSecurity6.0如何通过JWTtoken进行认证授权》:本文主要介绍SpringSecurity6.0通过JWTtoken进行认证授权的过程,本文给大家介绍的非常详细,感兴趣... 目录项目依赖认证UserDetailService生成JWT token权限控制小结之前写过一个文章,从S

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子