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

相关文章

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

2024年流动式起重机司机证模拟考试题库及流动式起重机司机理论考试试题

题库来源:安全生产模拟考试一点通公众号小程序 2024年流动式起重机司机证模拟考试题库及流动式起重机司机理论考试试题是由安全生产模拟考试一点通提供,流动式起重机司机证模拟考试题库是根据流动式起重机司机最新版教材,流动式起重机司机大纲整理而成(含2024年流动式起重机司机证模拟考试题库及流动式起重机司机理论考试试题参考答案和部分工种参考解析),掌握本资料和学校方法,考试容易。流动式起重机司机考试技

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

hdu 2093 考试排名(sscanf)

模拟题。 直接从教程里拉解析。 因为表格里的数据格式不统一。有时候有"()",有时候又没有。而它也不会给我们提示。 这种情况下,就只能它它们统一看作字符串来处理了。现在就请出我们的主角sscanf()! sscanf 语法: #include int sscanf( const char *buffer, const char *format, ... ); 函数sscanf()和

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同

ASIO网络调试助手之一:简介

多年前,写过几篇《Boost.Asio C++网络编程》的学习文章,一直没机会实践。最近项目中用到了Asio,于是抽空写了个网络调试助手。 开发环境: Win10 Qt5.12.6 + Asio(standalone) + spdlog 支持协议: UDP + TCP Client + TCP Server 独立的Asio(http://www.think-async.com)只包含了头文件,不依

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

poj 3181 网络流,建图。

题意: 农夫约翰为他的牛准备了F种食物和D种饮料。 每头牛都有各自喜欢的食物和饮料,而每种食物和饮料都只能分配给一头牛。 问最多能有多少头牛可以同时得到喜欢的食物和饮料。 解析: 由于要同时得到喜欢的食物和饮料,所以网络流建图的时候要把牛拆点了。 如下建图: s -> 食物 -> 牛1 -> 牛2 -> 饮料 -> t 所以分配一下点: s  =  0, 牛1= 1~

poj 3068 有流量限制的最小费用网络流

题意: m条有向边连接了n个仓库,每条边都有一定费用。 将两种危险品从0运到n-1,除了起点和终点外,危险品不能放在一起,也不能走相同的路径。 求最小的费用是多少。 解析: 抽象出一个源点s一个汇点t,源点与0相连,费用为0,容量为2。 汇点与n - 1相连,费用为0,容量为2。 每条边之间也相连,费用为每条边的费用,容量为1。 建图完毕之后,求一条流量为2的最小费用流就行了

poj 2112 网络流+二分

题意: k台挤奶机,c头牛,每台挤奶机可以挤m头牛。 现在给出每只牛到挤奶机的距离矩阵,求最小化牛的最大路程。 解析: 最大值最小化,最小值最大化,用二分来做。 先求出两点之间的最短距离。 然后二分匹配牛到挤奶机的最大路程,匹配中的判断是在这个最大路程下,是否牛的数量达到c只。 如何求牛的数量呢,用网络流来做。 从源点到牛引一条容量为1的边,然后挤奶机到汇点引一条容量为m的边