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

CCF-CSP认证考试 202312-4 宝藏 题解

一、题目介绍(CSDN截图)

二、思路及方法

  • 难点:1.直接模拟队列操作会超时,m,n可达10^{5} 

           2.队列操作是双端的,而且有删除操作

           3.支持单点修改

  • 思路:将每一条指令看作一个操作,可以对队列产生影响;

                  用线段树来维护区间上的指令复合效果;

                  将队列建模成两个栈,左栈(头部插入的矩阵)   右栈(尾部插入的矩阵),删除操作                会从两个栈中pop最近的那个

三、代码展示(此代码还不完全正确,对m,n<=1000适用)

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;const int MOD = 998244353;struct Matrix {long long a, b, c, d;Matrix() : a(1), b(0), c(0), d(1) {}Matrix(long long a_, long long b_, long long c_, long long d_) : a(a_ % MOD), b(b_ % MOD), c(c_ % MOD), d(d_ % MOD) {}
};Matrix mul(const Matrix& A, const Matrix& B) {return Matrix((A.a * B.a + A.b * B.c) % MOD,(A.a * B.b + A.b * B.d) % MOD,(A.c * B.a + A.d * B.c) % MOD,(A.c * B.b + A.d * B.d) % MOD);
}Matrix IDENTITY() {return Matrix(1, 0, 0, 1);
}struct Op {int type; // 1: L, 2: R, 3: DMatrix mat;
};struct Node {vector<Matrix> left_mats;  // 头部插入的矩阵(新到旧)vector<Matrix> right_mats; // 尾部插入的矩阵(旧到新)int delete_count;Node() : delete_count(0) {}
};vector<Op> instr;
vector<Node> seg;
int n, m;// 计算队列的乘积
Matrix calc_product(const Node& node) {vector<Matrix> queue;// 头部插入的矩阵要逆序(因为插入顺序是新到旧,但队列顺序是旧到新)for (int i = node.left_mats.size() - 1; i >= 0; i--) {queue.push_back(node.left_mats[i]);}// 尾部插入的顺序就是队列顺序for (const auto& mat : node.right_mats) {queue.push_back(mat);}if (queue.empty()) return IDENTITY();Matrix res = queue[0];for (int i = 1; i < queue.size(); i++) {res = mul(res, queue[i]);}return res;
}Node merge(const Node& left, const Node& right) {Node res;// 复制左边的矩阵vector<Matrix> left_stack = left.left_mats;vector<Matrix> right_stack = left.right_mats;// 执行右边的操作// 1. 右边的头部插入for (const auto& mat : right.left_mats) {left_stack.push_back(mat);}// 2. 右边的尾部插入  for (const auto& mat : right.right_mats) {right_stack.push_back(mat);}// 3. 处理删除操作int del_count = left.delete_count + right.delete_count;// 删除操作:优先删除左边栈(头部),然后右边栈(尾部)while (del_count > 0 && (!left_stack.empty() || !right_stack.empty())) {if (!left_stack.empty()) {left_stack.pop_back();} else {right_stack.pop_back();}del_count--;}res.left_mats = left_stack;res.right_mats = right_stack;res.delete_count = 0; // 删除操作已经处理完毕return res;
}void build(int idx, int l, int r) {if (l == r) {Node& node = seg[idx];if (instr[l].type == 1) {node.left_mats.push_back(instr[l].mat);} else if (instr[l].type == 2) {node.right_mats.push_back(instr[l].mat);} else {node.delete_count = 1;}return;}int mid = (l + r) / 2;build(idx * 2, l, mid);build(idx * 2 + 1, mid + 1, r);seg[idx] = merge(seg[idx * 2], seg[idx * 2 + 1]);
}void update(int idx, int l, int r, int pos) {if (l == r) {Node& node = seg[idx];node.left_mats.clear();node.right_mats.clear();node.delete_count = 0;if (instr[l].type == 1) {node.left_mats.push_back(instr[l].mat);} else if (instr[l].type == 2) {node.right_mats.push_back(instr[l].mat);} else {node.delete_count = 1;}return;}int mid = (l + r) / 2;if (pos <= mid) update(idx * 2, l, mid, pos);else update(idx * 2 + 1, mid + 1, r, pos);seg[idx] = merge(seg[idx * 2], seg[idx * 2 + 1]);
}Node query(int idx, int l, int r, int ql, int qr) {if (ql <= l && r <= qr) return seg[idx];int mid = (l + r) / 2;if (qr <= mid) return query(idx * 2, l, mid, ql, qr);if (ql > mid) return query(idx * 2 + 1, mid + 1, r, ql, qr);Node left = query(idx * 2, l, mid, ql, qr);Node right = query(idx * 2 + 1, mid + 1, r, ql, qr);return merge(left, right);
}int main() {ios::sync_with_stdio(false);cin.tie(0);cin >> n >> m;instr.resize(n);for (int i = 0; i < n; i++) {int t;cin >> t;if (t == 1) {long long a, b, c, d;cin >> a >> b >> c >> d;instr[i] = {1, Matrix(a, b, c, d)};} else if (t == 2) {long long a, b, c, d;cin >> a >> b >> c >> d;instr[i] = {2, Matrix(a, b, c, d)};} else {instr[i] = {3, IDENTITY()};}}seg.resize(4 * n);build(1, 0, n - 1);while (m--) {int type;cin >> type;if (type == 1) {int i;cin >> i;i--;int t;cin >> t;if (t == 1) {long long a, b, c, d;cin >> a >> b >> c >> d;instr[i] = {1, Matrix(a, b, c, d)};} else if (t == 2) {long long a, b, c, d;cin >> a >> b >> c >> d;instr[i] = {2, Matrix(a, b, c, d)};} else {instr[i] = {3, IDENTITY()};}update(1, 0, n - 1, i);} else {int l, r;cin >> l >> r;l--; r--;Node res = query(1, 0, n - 1, l, r);Matrix ans = calc_product(res);cout << ans.a << " " << ans.b << " " << ans.c << " " << ans.d << "\n";}}return 0;
}

四、代码详解

1.线段树节点设计

struct Node {

    vector<Matrix> left_mats;          // 模拟头部插入的矩阵(新到旧)

    vector<Matrix> right_mats;           // 模拟尾部插入的矩阵(旧到新)

    int delete_count;                     //删除操作的数量

   

    Node() : delete_count(0) {}

};

头部插入:新矩阵放在最前面

尾部插入:新矩阵放在最后面

删除最近插入:删除最近插入的矩阵

2.叶子节点构建

 if (instr[l].type == 1) {

   //头部插入:加入到left_mats中

            node.left_mats.push_back(instr[l].mat);

        } else if (instr[l].type == 2) {

     //尾部插入:加入到right_mats中

            node.right_mats.push_back(instr[l].mat);

        } else {

      //记录删除数量

            node.delete_count = 1;

        }

3.合并操作(核心)

Node merge(const Node& left, const Node& right) {

    Node res;

   

    // 复制左节点的所有矩阵

    vector<Matrix> left_stack = left.left_mats;

    vector<Matrix> right_stack = left.right_mats;

   

    // 执行右边的操作

    // 1. 右边的头部插入

    for (const auto& mat : right.left_mats) {

        left_stack.push_back(mat);

    }

    // 2. 右边的尾部插入  

    for (const auto& mat : right.right_mats) {

        right_stack.push_back(mat);

    }

    // 3. 处理删除操作

    int del_count = left.delete_count + right.delete_count;

   

    // 删除操作:优先删除左边栈(头部),然后右边栈(尾部)

    while (del_count > 0 && (!left_stack.empty() || !right_stack.empty())) {

        if (!left_stack.empty()) {

            left_stack.pop_back();       //删除最近的头部

        } else {

            right_stack.pop_back();          //删除最近的尾部

        }

        del_count--;

    }

   

    res.left_mats = left_stack;

    res.right_mats = right_stack;

    res.delete_count = 0; // 删除操作已经处理完毕

   

    return res;

}

4..对最后队列中所有矩阵的乘积算法

队列为空,则输出单位矩阵;队列为1,则输出该矩阵;队列长度大于1,则计算乘积

Matrix calc_product(const Node& node) {

    vector<Matrix> queue;

    // 头部插入的矩阵要逆序(因为插入顺序是新到旧,但队列顺序是旧到新)

    for (int i = node.left_mats.size() - 1; i >= 0; i--) {

        queue.push_back(node.left_mats[i]);

    }

    // 尾部插入的顺序就是队列顺序

    for (const auto& mat : node.right_mats) {

        queue.push_back(mat);

    }

   

    if (queue.empty()) return IDENTITY();

   

    Matrix res = queue[0];

    for (int i = 1; i < queue.size(); i++) {

        res = mul(res, queue[i]);

    }

    return res;

}

5.建树

void build(int idx, int l, int r) {

    if (l == r) {

   //叶子节点:处理单条指令

        Node& node = seg[idx];

        if (instr[l].type == 1) {

            node.left_mats.push_back(instr[l].mat);

        } else if (instr[l].type == 2) {

            node.right_mats.push_back(instr[l].mat);

        } else {

            node.delete_count = 1;

        }

        return;

    }

    int mid = (l + r) / 2;

    build(idx * 2, l, mid);                 //递归建左子树

    build(idx * 2 + 1, mid + 1, r);          //递归建右子树

    seg[idx] = merge(seg[idx * 2], seg[idx * 2 + 1]);         //合并左右子树的结果

}

6.更新

void update(int idx, int l, int r, int pos) {

    if (l == r) {

    //找到目标叶子节点,重新设置

        Node& node = seg[idx];

        node.left_mats.clear();

        node.right_mats.clear();

        node.delete_count = 0;

       

        if (instr[l].type == 1) {

            node.left_mats.push_back(instr[l].mat);         

        } else if (instr[l].type == 2) {

            node.right_mats.push_back(instr[l].mat);

        } else {

            node.delete_count = 1;

        }

        return;

    }

    int mid = (l + r) / 2;

    if (pos <= mid) update(idx * 2, l, mid, pos);        //在左子树

    else update(idx * 2 + 1, mid + 1, r, pos);         //在右子树

    seg[idx] = merge(seg[idx * 2], seg[idx * 2 + 1]);      //更新当前节点(重新合并子节点)

}

7.查询

Node query(int idx, int l, int r, int ql, int qr) {

   //当前区间完全包含在查询区间内

    if (ql <= l && r <= qr) return seg[idx];  

    int mid = (l + r) / 2;

    if (qr <= mid) return query(idx * 2, l, mid, ql, qr);  //完全在左子树

    if (ql > mid) return query(idx * 2 + 1, mid + 1, r, ql, qr);    //完全在右子树

  //跨区间查询

    Node left = query(idx * 2, l, mid, ql, qr);

    Node right = query(idx * 2 + 1, mid + 1, r, ql, qr);

    return merge(left, right);

}

五、总结

线段树是一种二叉树,用于高效处理区间查询和区间更新问题。

想象你要管理一个数组,经常需要:

· 查询某个区间的和/最大值/最小值
· 修改某个位置的值

如果每次查询都遍历区间,时间复杂度是O(n)。线段树通过预处理和树形结构,把这些操作降到O(log n)。

http://www.dtcms.com/a/447178.html

相关文章:

  • 个人网站备案号被注销了网站运营优化推广
  • Python数据清洗实战指南
  • s网站优化西安咪豆网站建设公司
  • 怀柔建设网站公司公司网站后台维护怎么做
  • 网站开发 ssh 菜鸟东阳网络科技有限公司
  • 宁波网站建设费用网站运营岗位介绍
  • 公司的网 网站打不开怎么办网络营销宏观环境有哪些
  • 网站建设中扁平化结构chrome浏览器官网入口
  • 网站建设属于什么合同做网站游戏推广赚钱
  • asrpro2.0天问语音模块搭配STM32(STM32F103c8t6)-杨桃电子开发板
  • 网站发布时间更改wordpress大前端哪个好
  • php租车网站网站软件下载大全
  • LangChain 学习 - LangChain 引入(LangChain 概述、LangChain 的使用场景、LangChain 架构设计)
  • 门户网站是网络表达吗杭州建设网电焊工报名入口
  • 公司网站建设计入什么明细科目全国行业名录搜索系统
  • 设计网站企业网站建设公司成都哪家做网站做的好
  • 一如:真实的觉现
  • 做配色的网站百度竞价点击工具
  • 东莞品牌网站建设费用网站关键词重复
  • 郑州睿网站建设成都旧房改造装修公司哪家好
  • 网站 利润成都市建设网站公司
  • 宿州网站建设零聚思放心wordpress 博主认证
  • 设计网站如何推广门户网站app开发
  • 东莞php网站建设广元建设厅官方网站
  • ClaudeCode真经第六章:问题排查与故障处理
  • 网站建设找好景科技广州外贸公司有哪些
  • 威海专业做网站公司中囯联通腾迅
  • 动态规划 - 回文子串问题
  • C 标准库 - `<float.h>`
  • 第八章:组合模式 - 整体部分的统一大师