2022信奥赛C++提高组csp-s复赛真题及题解:星战

2022信奥赛C++提高组csp-s复赛真题及题解:星战

题目描述

在这一轮的星际战争中,我方在宇宙中建立了 n n n 个据点,以 m m m 个单向虫洞连接。我们把终点为据点 u u u 的所有虫洞归为据点 u u u 的虫洞。

战火纷飞之中这些虫洞很难长久存在,敌人的打击随时可能到来。这些打击中的有效打击可以分为两类:

  1. 敌人会摧毁某个虫洞,这会使它连接的两个据点无法再通过这个虫洞直接到达,但这样的打击无法摧毁它连接的两个据点。
  2. 敌人会摧毁某个据点,由于虫洞的主要技术集中在出口处,这会导致该据点的所有还未被摧毁的虫洞被一同摧毁。而从这个据点出发的虫洞则不会摧毁

注意:摧毁只会导致虫洞不可用,而不会消除它的存在。

为了抗击敌人并维护各部队和各据点之间的联系,我方发展出了两种特种部队负责修复虫洞:

  • A 型特种部队则可以将某个特定的虫洞修复。
  • B 型特种部队可以将某据点的所有损坏的虫洞修复。

考虑到敌人打击的特点,我方并未在据点上储备过多的战略物资。因此只要这个据点的某一条虫洞被修复,处于可用状态,那么这个据点也是可用的。

我方掌握了一种苛刻的空间特性,利用这一特性我方战舰可以沿着虫洞瞬移到敌方阵营,实现精确打击。

为了把握发动反攻的最佳时机,指挥部必须关注战场上的所有变化,为了寻找一个能够进行反攻的时刻。总指挥认为:

  • 如果从我方的任何据点出发,在选择了合适的路线的前提下,可以进行无限次的虫洞穿梭(可以多次经过同一据点或同一虫洞),那么这个据点就可以实现反击
  • 为了使虫洞穿梭的过程连续,尽量减少战舰在据点切换虫洞时的质能损耗,当且仅当只有一个从该据点出发的虫洞可用 时,这个据点可以实现连续穿梭
  • 如果我方所有据点都可以实现反击 ,也都可以实现连续穿梭 ,那么这个时刻就是一个绝佳的反攻时刻。

总司令为你下达命令,要求你根据战场上实时反馈的信息,迅速告诉他当前的时刻是否能够进行一次反攻

输入格式

输入的第一行包含两个正整数 n , m n,m n,m。

接下来 m m m 行每行两个数 u , v u,v u,v,表示一个从据点 u u u 出发到据点 v v v 的虫洞。保证 u ≠ v u \ne v u=v,保证不会有两条相同的虫洞。初始时所有的虫洞和据点都是完好的。

接下来一行一个正整数 q q q 表示询问个数。

接下来 q q q 行每行表示一次询问或操作。首先读入一个正整数 t t t 表示指令类型:

  • 若 t = 1 t = 1 t=1,接下来两个整数 u , v u, v u,v 表示敌人摧毁了从据点 u u u 出发到据点 v v v 的虫洞。保证该虫洞存在且未被摧毁。
  • 若 t = 2 t = 2 t=2,接下来一个整数 u u u 表示敌人摧毁了据点 u u u。如果该据点的虫洞已全部被摧毁,那么这次袭击不会有任何效果。
  • 若 t = 3 t = 3 t=3,接下来两个整数 u , v u, v u,v 表示我方修复了从据点 u u u 出发到据点 v v v 的虫洞。保证该虫洞存在且被摧毁。
  • 若 t = 4 t = 4 t=4,接下来一个整数 u u u 表示我方修复了据点 u u u。如果该据点没有被摧毁的虫洞,那么这次修复不会有任何效果。

在每次指令执行之后,你需要判断能否进行一次反攻。如果能则输出 YES 否则输出 NO

输出格式

输出一共 q q q 行。对于每个指令,输出这个指令执行后能否进行反攻。

输入输出样例 #1
输入 #1
复制代码
3 6
2 3
2 1
1 2
1 3
3 1
3 2
11
1 3 2
1 2 3
1 1 3
1 1 2
3 1 3
3 3 2
2 3
1 3 1
3 1 3
4 2
1 3 2
输出 #1
复制代码
NO
NO
YES
NO
YES
NO
NO
NO
YES
NO
NO
说明/提示

【样例解释 #1】

虫洞状态可以参考下面的图片, 图中的边表示存在且未被摧毁的虫洞:

【数据范围】

对于所有数据保证: 1 ≤ n ≤ 5 × 10 5 1 \le n \le 5 \times {10}^5 1≤n≤5×105, 1 ≤ m ≤ 5 × 10 5 1 \le m \le 5 \times {10}^5 1≤m≤5×105, 1 ≤ q ≤ 5 × 10 5 1 \le q \le 5 \times {10}^5 1≤q≤5×105。

测试点 n ≤ n \le n≤ m ≤ m \le m≤ q ≤ q \le q≤ 特殊限制
1 ∼ 3 1 \sim 3 1∼3 10 10 10 20 20 20 50 50 50
4 ∼ 8 4 \sim 8 4∼8 10 3 {10}^3 103 10 4 {10}^4 104 10 3 {10}^3 103
9 ∼ 10 9 \sim 10 9∼10 5 × 10 5 5 \times {10}^5 5×105 5 × 10 5 5 \times {10}^5 5×105 5 × 10 5 5 \times {10}^5 5×105 保证没有 t = 2 t = 2 t=2 和 t = 4 t = 4 t=4 的情况
11 ∼ 12 11 \sim 12 11∼12 5 × 10 5 5 \times {10}^5 5×105 5 × 10 5 5 \times {10}^5 5×105 5 × 10 5 5 \times {10}^5 5×105 保证没有 t = 4 t = 4 t=4 的情况
13 ∼ 16 13 \sim 16 13∼16 10 5 {10}^5 105 5 × 10 5 5 \times {10}^5 5×105 5 × 10 5 5 \times {10}^5 5×105
17 ∼ 20 17 \sim 20 17∼20 5 × 10 5 5 \times {10}^5 5×105 5 × 10 5 5\times 10^5 5×105 5 × 10 5 5 \times {10}^5 5×105

思路分析

问题转化

题目要求判断是否每个点出度恰好为1。这等价于:

  • 总边数 = n(n个点,每个点1条出边)
  • 每个点出度 ≥ 1(否则边数不足n)
随机哈希策略
  1. 给每个点分配随机权值w[i]
  2. 维护两个关键值
    • ans = Σw[i]:所有点权值之和
    • tot = Σw[u]:当前所有有效边的起点权值之和
  3. 判断条件
    • 如果tot == ans,则说明每个点的权值恰好被计算一次
    • 在随机权值下,这等价于每个点出度恰好为1
操作维护
  1. 操作1(删除边u→v)

    • 从目标点v的入边权值和中减去起点u的权值
    • 总权值和tot减去w[u]
  2. 操作2(摧毁据点u)

    • tot中减去当前所有指向u的边的权值和
    • 将u的入边权值和置零
  3. 操作3(修复边u→v)

    • 在目标点v的入边权值和中加上起点u的权值
    • 总权值和tot加上w[u]
  4. 操作4(修复据点u)

    • 恢复u的原始入边权值和
    • 将修复的权值加到tot

代码实现

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 5e5 + 5;

int n, m, q;           // 点数、边数、操作数
int w[N];              // 每个点的随机权值
ll in[N];              // 当前入边权值和:in[v] = Σw[u] (存在边u→v)
ll backup[N];          // 备份的入边权值和
ll tot, target;        // 当前总权值和、目标总权值和

int main() {
    // 随机数生成器
    mt19937 rng(time(0));
    
    // 输入点数和边数
    scanf("%d%d", &n, &m);
    
    // 给每个点分配随机权值并计算目标值
    for (int i = 1; i <= n; i++) {
        w[i] = rng();              // 生成随机权值
        target += w[i];            // 累加目标总权值
    }
    
    // 输入初始边并初始化入边权值和
    for (int i = 1; i <= m; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        in[v] += w[u];             // 边u→v:将u的权值加到v的入边和中
        tot += w[u];               // 总权值累加
    }
    
    // 备份初始入边权值和
    for (int i = 1; i <= n; i++) {
        backup[i] = in[i];
    }
    
    // 处理操作
    scanf("%d", &q);
    while (q--) {
        int type, u, v;
        scanf("%d%d", &type, &u);
        
        if (type == 1) {
            // 删除边u→v
            scanf("%d", &v);
            in[v] -= w[u];         // 从v的入边和中减去u的权值
            tot -= w[u];           // 总权值减少
        }
        else if (type == 2) {
            // 摧毁据点u:删除所有指向u的边
            tot -= in[u];          // 减去当前所有指向u的边的权值和
            in[u] = 0;             // 清空u的入边权值和
        }
        else if (type == 3) {
            // 修复边u→v
            scanf("%d", &v);
            in[v] += w[u];         // 向v的入边和中添加u的权值
            tot += w[u];           // 总权值增加
        }
        else { // type == 4
            // 修复据点u:恢复所有指向u的边
            tot += backup[u] - in[u];  // 修复损坏的入边
            in[u] = backup[u];         // 恢复原始入边权值和
        }
        
        // 判断是否满足条件并输出结果
        puts(tot == target ? "YES" : "NO");
    }
    
    return 0;
}

功能分析

核心变量说明
变量名 类型 描述
w[i] int 点i的随机权值
in[i] ll 当前指向点i的边的起点权值之和
backup[i] ll 点i的初始入边权值和备份
tot ll 当前所有有效边的起点权值之和
target ll 目标总权值(所有点权值之和)
操作时间复杂度
  • 操作1和3:O(1) - 只更新一条边
  • 操作2和4:O(1) - 通过备份实现批量操作
  • 判断操作:O(1) - 仅需比较两个值
空间复杂度
  • O(n):需要存储每个点的权值和入边权值和
  • O(m):仅读取边信息,不存储完整图结构
正确性保障
  1. 随机性保障

    • 使用mt19937生成32位随机数
    • 冲突概率极低(约1/4,294,967,296)
    • 实际测试中未发现冲突
  2. 操作正确性

    • 操作1和3对称,保证单条边的正确更新
    • 操作2和4通过备份机制,避免遍历所有入边
    • 总权值tot始终与当前图状态一致
  3. 条件等价性

    • 当且仅当每个点出度为1时,tot == target
    • 如果tot == target但出度不为1,则是哈希冲突,概率极低
注意事项
  1. 随机种子:使用时间种子,不同运行结果可能不同,但单次运行内一致
  2. 整数范围 :使用long long避免溢出

专栏推荐:信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新)
https://blog.csdn.net/weixin_66461496/category_13125089.html


各种学习资料,助力大家一站式学习和提升!!!

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"##########  一站式掌握信奥赛知识!  ##########";
	cout<<"#############  冲刺信奥赛拿奖!  #############";
	cout<<"######  课程购买后永久学习,不受限制!   ######";
	return 0;
}

1、csp信奥赛高频考点知识详解及案例实践:

CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转

CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转

信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html

2、csp信奥赛冲刺一等奖有效刷题题解:

CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新):https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转

CSP信奥赛C++一等奖通关刷题题单及题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12673810.html 点击跳转

3、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html

4、CSP信奥赛C++竞赛拿奖视频课:

https://edu.csdn.net/course/detail/40437 点击跳转

· 文末祝福 ·

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"跟着王老师一起学习信奥赛C++";
	cout<<"    成就更好的自己!       ";
	cout<<"  csp信奥赛一等奖属于你!   ";
	return 0;
}
相关推荐
兩尛2 小时前
2. 两数相加 c++
开发语言·c++
j445566112 小时前
C++中的备忘录模式
开发语言·c++·算法
近津薪荼2 小时前
dfs专题——二叉树的深搜3(二叉树剪枝)
c++·学习·算法·深度优先
卷卷的小趴菜学编程2 小时前
项目篇----仿tcmalloc的内存池设计(page cache)
c++·缓存·单例模式·tcmalloc·内存池·span cache
m0_706653232 小时前
C++中的解释器模式
开发语言·c++·算法
王老师青少年编程2 小时前
2022信奥赛C++提高组csp-s复赛真题及题解:数据传输
c++·数据传输·真题·csp·信奥赛·csp-s·提高组
hetao17338372 小时前
2026-01-29~02-03 hetao1733837 的刷题记录
c++·笔记·算法
晚风吹长发2 小时前
初步了解Linux中的POSIX信号量及环形队列的CP模型
linux·运维·服务器·数据结构·c++·算法
近津薪荼2 小时前
优选算法——前缀和(1):一维前缀和
c++·学习·算法