首页 > 其他分享 >[THUSCH2017] 大魔法师

[THUSCH2017] 大魔法师

时间:2024-02-28 22:49:00浏览次数:21  
标签:end Matrix int 矩阵 魔法师 bmatrix THUSCH2017 水晶球

THUSCH2017] 大魔法师

题目描述

大魔法师小 L 制作了 $n$ 个魔力水晶球,每个水晶球有水、火、土三个属性的能量值。小 L 把这 $n$ 个水晶球在地上从前向后排成一行,然后开始今天的魔法表演。

我们用 $A_i,B_i,C_i$ 分别表示从前向后第 $i$ 个水晶球(下标从 $1$ 开始)的水、火、土的能量值。

小 L 计划施展 $m$ 次魔法。每次,他会选择一个区间 $[l,r]$,然后施展以下 $3$ 大类、$7$ 种魔法之一:

  1. 魔力激发:令区间里每个水晶球中特定属性的能量爆发,从而使另一个特定属性的能量增强。具体来说,有以下三种可能的表现形式:
    • 火元素激发水元素能量:令 $A_i=A_i+B_i$。
    • 土元素激发火元素能量:令 $B_i=B_i+C_i$。
    • 水元素激发土元素能量:令 $C_i=C_i+A_i$。
  2. 魔力增强:小 L 挥舞法杖,消耗自身 $v$ 点法力值,来改变区间里每个水晶球的**特定属性**的能量。具体来说,有以下三种可能的表现形式:
    • 火元素能量定值增强:令 $A_i=A_i+v$。
    • 水元素能量翻倍增强:令 $B_i=B_i\times v$。
    • 土元素能量吸收融合:令 $C_i=v$。
  3. 魔力释放:小 L 将区间里所有水晶球的能量聚集在一起,融合成一个新的水晶球,然后送给场外观众。生成的水晶球每种属性的能量值等于区间内所有水晶球对应能量值的代数和。需要注意的是,魔力释放的过程不会真正改变区间内水晶球的能量。

值得一提的是,小 L 制造和融合的水晶球的原材料都是定制版的 OI 工厂水晶,所以这些水晶球有一个能量阈值 $998244353$。当水晶球中某种属性的能量值大于等于这个阈值时,能量值会自动对阈值取模,从而避免水晶球爆炸。

小 W 为小 L(唯一的)观众,围观了整个表演,并且收到了小 L 在表演中融合的每个水晶球。小 W 想知道,这些水晶球蕴涵的三种属性的能量值分别是多少。

输入格式

从标准输入读入数据。

我们将上述的 $7$ 种魔法,从上到下依次标号为 $1\sim7$。

输入的第一行包含一个整数 $n$($1\le n\le 2.5\times 10^5$),表示水晶球个数。

接下来 $n$ 行,每行空格隔开的 $3$ 个整数,其中第 $i$ 行的三个数依次表示 $A_i,B_i,C_i$。

接下来一行包含一个整数 $m$($1\le m\le2.5\times 10^5$),表示施展魔法的次数。

接下来 $m$ 行,每行 $3$ 或 $4$ 个数,格式为 opt l r (v)。其中 opt 表示魔法的编号,$l,r$ 表示施展魔法的区间(保证有 $l\le r$)。特别地,如果施展 $4\sim6$ 号魔法(魔力增强),则还有一个整数 $v$,表示小 L 消耗的法力值。

输出格式

输出到标准输出。

对每个 $7$ 号魔法(魔力释放),输出一行、空格隔开的 $3$ 个整数 `a b c`,分别表示此次融合得到的水晶球的水、火、土元素能量值。

样例 #1

样例输入 #1

2
2 3 3
6 6 6
4
7 1 2
1 1 2
4 1 2 3
7 1 2

样例输出 #1

8 9 9
23 9 9

 

提示

$100\%$ 的数据,$n,m\le2.5\times 10^5,0\le A_i,B_i,C_i,v<998244353$

  1. $10\%$ 的数据,$n\times m\le10^7$。
  2. 另外 $10\%$ 的数据,每次魔法的区间均为 $[1,n]$。
  3. 另外 $10\%$ 的数据,每次非询问魔法的影响区间均为 $[1,n]$,所有修改在询问之前。
  4. 另外 $10\%$ 的数据,$\operatorname{opt}\in\{4,5,6,7\}$。
  5. 另外 $15\%$ 的数据,$\operatorname{opt}\in\{1,2,7\}$。
  6. 另外 $15\%$ 的数据,$\operatorname{opt}\in\{1,2,3,5,7\}$。
  7. 另外 $15\%$ 的数据,$n,m\le 10^5$。
  8. 其他数据,无特殊约定。

样例解释

以下展示每次施展魔法后,两个水晶球内的能量:

(2, 3, 3) (6, 6, 6)
(5, 3, 3) (12, 6, 6)
(8, 3, 3) (15, 6, 6)
(8, 3, 3) (15, 6, 6)

 

解题思路

  显然线段树需要分别维护区间内 $a_i$,$b_i$,$c_i$ 的总和,如果只有第 $4 \sim 6$ 种修改操作显然带懒标记的区间修改是可以做的,但对于第 $1 \sim 3$ 种操作每次修改的值都会依赖另外一个变量,就不容易用区间修改加懒标记去维护,除非强制改成单点修改。这时候就可以考虑用矩阵去维护变量了,修改操作就相当于右乘上一个转移矩阵。

  定义 $f_i = \begin{bmatrix} a_i & b_i & c_i & 1 \end{bmatrix}$ 表示第 $i$ 个水晶球对应的矩阵。接下来分析每种操作对应的转移矩阵是什么。

  对于第 $1$ 种操作,修改后矩阵应该变成 $f_i' = \begin{bmatrix} a_i + b_i & b_i & c_i & 1 \end{bmatrix}$,对应的转移矩阵为 $A_1 = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 1 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}$,那么就会有 $f_i \times A_1 = f_i'$。

  对于第 $2$ 种操作,修改后矩阵应该变成 $f_i' = \begin{bmatrix} a_i & b_i + c_i & c_i & 1 \end{bmatrix}$,对应的转移矩阵为 $A_2 = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 1 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}$,那么就会有 $f_i \times A_2 = f_i'$。

  对于第 $3$ 种操作,修改后矩阵应该变成 $f_i' = \begin{bmatrix} a_i & b_i & c_i + a_i & 1 \end{bmatrix}$,对应的转移矩阵为 $A_3 = \begin{bmatrix} 1 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}$,那么就会有 $f_i \times A_3 = f_i'$。

  对于第 $4$ 种操作,修改后矩阵应该变成 $f_i' = \begin{bmatrix} a_i + v & b_i & c_i & 1 \end{bmatrix}$,对应的转移矩阵为 $A_4 = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ v & 0 & 0 & 1 \end{bmatrix}$,那么就会有 $f_i \times A_4 = f_i'$。

  对于第 $5$ 种操作,修改后矩阵应该变成 $f_i' = \begin{bmatrix} a_i & b_i \cdot v & c_i & 1 \end{bmatrix}$,对应的转移矩阵为 $A_5 = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & v & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}$,那么就会有 $f_i \times A_5 = f_i'$。

  对于第 $6$ 种操作,修改后矩阵应该变成 $f_i' = \begin{bmatrix} a_i & b_i & v & 1 \end{bmatrix}$,对应的转移矩阵为 $A_6 = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & v & 1 \end{bmatrix}$,那么就会有 $f_i \times A_6 = f_i'$。

  因此线段树维护的是区间内矩阵的和,由于矩阵乘法具有分配律,每次修改操作相当于给区间乘上对应的转移矩阵。懒标记自然维护的就是矩阵的累乘,初始化为单位矩阵 $A_0 = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}$。

  然后就是经典卡常了,只需在矩阵乘法中减少取模运算即可,具体见代码。

  AC 代码如下,时间复杂度为 $O(n + m \log{n})$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 250010, mod = 998244353;

int a[N], b[N], c[N];
struct Matrix {
    array<array<int, 4>, 4> a;
    
    Matrix(array<array<int, 4>, 4> b = {0}) {
        a = b;
    }
    auto& operator[](int x) {
        return a[x];
    }
    Matrix operator+(Matrix b) {
        Matrix c;
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                c[i][j] = (a[i][j] + b[i][j]) % mod;
            }
        }
        return c;
    }
    Matrix operator*(Matrix b) {
        Matrix c;
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                // 直接一次求出c[i][j]的结果再取模
                c[i][j] = ((LL)a[i][0] * b[0][j] + (LL)a[i][1] * b[1][j] + (LL)a[i][2] * b[2][j] + (LL)a[i][3] * b[3][j]) % mod;
            }
        }
        return c;
    }
    bool operator==(Matrix &b) {
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                if (a[i][j] != b[i][j]) return false;
            }
        }
        return true;
    }
}g[7];
struct Node {
    int l, r;
    Matrix p, f;
}tr[N * 4];

void build(int u, int l, int r) {
    tr[u] = {l, r, g[0]};
    if (l == r) {
        tr[u].f = Matrix({a[l], b[l], c[l], 1});
    }
    else {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        tr[u].f = tr[u << 1].f + tr[u << 1 | 1].f;
    }
}

void pushdown(int u) {
    if (tr[u].f == g[0]) return;
    tr[u << 1].f = tr[u << 1].f * tr[u].p;
    tr[u << 1].p = tr[u << 1].p * tr[u].p;
    tr[u << 1 | 1].f = tr[u << 1 | 1].f * tr[u].p;
    tr[u << 1 | 1].p = tr[u << 1 | 1].p * tr[u].p;
    tr[u].p = g[0];
}

void modify(int u, int l, int r, Matrix &a) {
    if (tr[u].l >= l && tr[u].r <= r) {
        tr[u].f = tr[u].f * a;
        tr[u].p = tr[u].p * a;
    }
    else {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(u << 1, l, r, a);
        if (r >= mid + 1) modify(u << 1 | 1, l, r, a);
        tr[u].f = tr[u << 1].f + tr[u << 1 | 1].f;
    }
}

Matrix query(int u, int l, int r) {
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].f;
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    Matrix ret;
    if (l <= mid) ret = query(u << 1, l, r);
    if (r >= mid + 1) ret = ret + query(u << 1 | 1, l, r);
    return ret;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i] >> b[i] >> c[i];
    }
    g[0] = Matrix({1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1});    // 单位矩阵
    g[1] = Matrix({1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1});    // 操作1对应的转移矩阵
    g[2] = Matrix({1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1});    // 操作2对应的转移矩阵
    g[3] = Matrix({1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1});    // 操作3对应的转移矩阵
    g[4] = Matrix({1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1});    // 操作4对应的转移矩阵
    g[5] = Matrix({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1});    // 操作5对应的转移矩阵
    g[6] = Matrix({1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1});    // 操作6对应的转移矩阵
    build(1, 1, n);
    cin >> m;
    while (m--) {
        int op, l, r, v;
        cin >> op >> l >> r;
        if (op < 7) {
            if (op > 3) {    // 对于操作4~6,需要在转移矩阵相应的位置改成v
                cin >> v;
                if (op == 4) g[4][3][0] = v;
                else if (op == 5) g[5][1][1] = v;
                else g[6][3][2] = v;
            }
            modify(1, l, r, g[op]);
        }
        else {
            Matrix ret = query(1, l, r);
            cout << ret[0][0] << ' ' << ret[0][1] << ' ' << ret[0][2] << '\n';
        }
    }
    
    return 0;
}

 

参考资料

  线段树+矩阵乘法:https://www.luogu.com.cn/article/qf6az4pt

标签:end,Matrix,int,矩阵,魔法师,bmatrix,THUSCH2017,水晶球
From: https://www.cnblogs.com/onlyblues/p/18041967

相关文章

  • [THUSCH2017] 巧克力
    [THUSCH2017]巧克力题目描述「人生就像一盒巧克力,你永远不知道吃到的下一块是什么味道。」明明收到了一大块巧克力,里面有若干小块,排成\(n\)行\(m\)列。每一小块都有自己特别的图案,它们有的是海星,有的是贝壳,有的是海螺……其中还有一些因为挤压,已经分辨不出是什么图案了。......
  • 配置隧道代理HTTP:手动设置与自动配置,一篇文章让你成为网络魔法师!
    嘿,小伙伴们!今天我们要一起探讨一个激动人心的话题——如何配置隧道代理HTTP。这个话题可能听起来有点复杂,但别担心,我会用最简单的方式为你解释。首先,让我们来了解一下什么是隧道代理HTTP。简单来说,它就像是一条魔法通道,能帮助我们更好地浏览网页、保护隐私、甚至突破地域限制。配置......
  • [THUSCH2017] 大魔法师
    前期准备1.熟练的掌握区间修改线段树2.对矩阵乘法有部分的了解,知道如何使用3.对卡常十分精通题目大意题目给定\(n\)个三元组,每个三元组包含\(A\)、\(B\)、\(C\)三个元素,一共进行\(m\)次操作,分别是下面七种之一:1.令给定区间内,\(A_i=A_i+B_i\)2.令给定区间内,\(B_i=B......
  • WebSocket魔法师:打造实时应用的无限可能
    1、背景在开发一些前端页面的时候,总是能接收到这样的需求:如何保持页面并实现自动更新数据呢?以往的常规做法,是前端使用定时轮询后端接口,获取响应后重新渲染前端页面,这种做法虽然能达到类似的效果,但是依然有很多缺点,缺点就不在这里说了,感兴趣的小伙伴可以自行查阅一下。现在让我们......
  • Capture One 23:RAW图像的魔法师,开启你的摄影艺术之旅 mac/win版
    CaptureOne23,这不仅仅是一款RAW图像编辑软件,更是一款为你开启摄影艺术之旅的魔法师。这个强大的工具将带你进入RAW图像的世界,让你自由地探索并创造出令人惊艳的摄影作品。无论你是专业摄影师,还是摄影爱好者,CaptureOne23都能根据你的需求提供全面的解决方案。→→↓↓载Captur......
  • std::forward:完美转发的魔法师
    大家好,今天我们来谈谈一个C++11引入的强大工具:std::forward。如果你曾经头疼于如何设计一个函数,让它能同时接受左值和右值,且能保留参数原始的性质,那么今天的主题绝对是你的救星。1、std::forward是什么?简单来说,std::forward是一种用于实现完美转发(PerfectForwarding)的机制。它......
  • P7450 [THUSCH2017] 巧克力
    P7450[THUSCH2017]巧克力题意给定一张网格图,每个格子有两个权重,\((a,c)\),我们希望找出一个不包含\(c=-1\)的联通块并且\(a\)的中位数最大,同时还要包含\(k\)种颜色。题解套路题都是nb题。首先\(k\)比较小,我们可以考虑一个类似斯坦纳树的\(dp\)。\(f_{i,j,S}\)表......
  • [THUSCH2017] 大魔法师 卡题记录
    题目:fzqoj-luogu前情提示: 此题极度卡常!!!,否则你就会像我这个蒟蒻一样卡题\(3h\):死亡记录前置知识:  1.线段树的区间修改,不会的可以点这-基础:进阶  2.基本的矩阵乘法:Fibonacci题解部分对于题目给出的6种操作,我们可以用线段树与矩阵乘法来维护思路维护一个四......
  • LonLife-ACM 1129 - 喵哈哈村的战斗魔法师丶坏坏い月
    原题链接1129-喵哈哈村的战斗魔法师丶坏坏い月TimeLimit:3s MemoryLimit:256MByteSolved:85DESCRIPTION坏坏い月是月大叔的ID,他是一个掌握者772002种魔法的物理系战士,最擅长的技能就是搞事。今天他又要开始搞事了。nn个数,你需要实现一下操作:lrv,在[l,r]......
  • 《花雕学AI》语言+想象+人工智能=图像魔法:微软 Bing 图像魔法师的功能、价值和评测
    你有没有想过,如果你能够用语言来创造图像,那该有多么神奇和有趣?你有没有想过,如果你能够看到你想象中的图像,那该有多么震撼和美妙?现在,这一切都可以实现了,因为微软Bing图像魔法师来了!微软Bing图像魔法师是一款能够根据用户的描述生成图像的人工智能产品,它可以让你的语言变成视觉,......