【洛谷5280】[ZJOI2019]线段树(线段树)

题目

洛谷5280

分析

一道加深对线段树理解的好题。(其实并没有什么用处)

注意,每次操作是先把所有线段树复制一份,只有旧的线段树进行了 Modify ,而新的线段树保持原样。

先转化一下题意。如果直接维护某一个结点在多少棵线段树上是有 tag 的,那么每次修改时,没有被访问到的结点答案都要乘 \(2\) ,时间复杂度会爆掉。而如果改成每次有 \(\frac{1}{2}\) 的概率进行修改,维护某一个结点有 tag 的概率(相当于原题中该结点有 tag 的树的数量占总数之比),没有被访问到的结点就不需要修改了。

现在,设 \(u\) 有 tag 的概率是 \(f_{u,0}\) ,来仔细分析一下 Modify 时发生了什么。出于某种原因(以下会提到),还要维护 \(f_{u,1}\) 表示从根到 \(u\) 的路径上(不含 \(u\) )有 tag 且 \(u\) 没有 tag 的概率。方便起见,再设 \(f_{u,2}=1-f_{u,0}-f_{u,1}\)

对于 直接 修改到的结点(就是被修改区间完全覆盖而父亲不被修改区间完全覆盖的结点),如果原来有 tag (概率为\(f_{u,0}\) )还有 tag ,对于原来没有 tag 的情况(概率为 \(1-f_{u,0}\))有 \(\frac{1}{2}\) 的概率被修改而打上 tag ,即:

\[f'_{u,0}=f_{u,0}+\frac{1-f_{u,0}}{2}\]

对于直接修改到的结点的父亲(就是与修改区间有交但是不被完全覆盖的结点),被 pushdown 强制去掉了 tag 。也就是当原来有 tag 且本次修改没有进行时才有 tag ,即:

\[f'_{u,0}=\frac{f_{u,0}}{2}\]

对于父亲属于第二类点而本身不与修改区间有交的点,这些点接收了来自父亲的 pushdown ,因此如果修改前在从根到它的路径上(不含本身)有 tag 那么它就会被打上 tag ,否则如果它自己本来有 tag 就有 tag ,否则没有,即:

\[f'_{u,0}=\frac{f'_{u,1}}{2}+f'_{u,0}\]

以上三种情况中的结点的祖先都全部被 pushdown 了,所以只有当修改没有发生时从根到这些结点的路径上才可能有 tag ,即:

\[f'_{u,1}=\frac{f_{u,1}}{2}\]

对于直接修改到的结点子树中的点(不含自身)(就是被修改区间完全覆盖且父亲也被修改区间完全覆盖的结点),tag 的有无没有发生变化,而如果修改成功进行了,这些结点全部都满足到根的路径上有 tag (因为直接修改到的结点一定有 tag ),但要排除掉这个点本身有 tag 的情况 (请回去看 \(f_{u,1}\) 的定义),即:

\[f'_{u,0}=f_{u,0}\]

\[f'_{u,1}=\frac{1-f_{u,0}}{2}+\frac{f_{u,1}}{2}=f_{u,1}+\frac{f_{u,2}}{2}\]

\[f'_{u,2}=\frac{f'_{u,2}}{2}\]

其他结点的 tag 不会有变化,从它到根的路径上的 tag 的 存在性 也没有变化(位置可能被 pushdown 了),所以什么都不需要改。

综合以上分析,前三类的总数是 \(O(\log n)\) ,可以直接修改;第五类不需要改。而对于第四类,每次修改的是一棵子树,且只修改 \(f_{u,1}\)\(f_{u,2}\) ,一次修改可以转化为一次矩阵乘法:

\[\begin{bmatrix}f_{u,1}&f_{u,2}\end{bmatrix} \begin{bmatrix}1&0\\\frac{1}{2}&\frac{1}{2}\end{bmatrix}= \begin{bmatrix}f'_{u,1}&f'_{u,2}\end{bmatrix}\]

这样,可以在线段树上打 tag (与题目中的 tag 无关)记录子树修改次数,每次修改的时候乘若干次矩阵即可。矩阵的幂可以预处理。

代码

代码是很久以前写的,里面 f1 和 f2 和上面的定义是相反的,凑活看吧 ……

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;

namespace zyt
{
    template<typename T>
    inline bool read(T &x)
    {
        char c;
        bool f = false;
        x = 0;
        do
            c = getchar();
        while (c != EOF && c != '-' && !isdigit(c));
        if (c == EOF)
            return false;
        if (c == '-')
            f = true, c = getchar();
        do
            x = x * 10 + c - '0', c = getchar();
        while (isdigit(c));
        if (f)
            x = -x;
        return true;
    }
    template<typename T>
    inline void write(T x)
    {
        static char buf[20];
        char *pos = buf;
        if (x < 0)
            putchar('-'), x = -x;
        do
            *pos++ = x % 10 + '0';
        while (x /= 10);
        while (pos > buf)
            putchar(*--pos);
    }
    typedef long long ll;
    const int N = 1e5 + 10, P = 998244353;
    int n, inv2;
    int power(int a, int b)
    {
        int ans = 1;
        while (b)
        {
            if (b & 1)
                ans = (ll)ans * a % P;
            a = (ll)a * a % P;
            b >>= 1;
        }
        return ans;
    }
    int inv(const int a)
    {
        return power(a, P - 2);
    }
    class Matrix
    {
    private:
        int n, m;
    public:
        int data[2][2];
        Matrix(const int _n = 0, const int _m = 0)
            : n(_n), m(_m)
        {
            for (int i = 0; i < n; i++)
                memset(data[i], 0, sizeof(int[m]));
        }
        Matrix operator * (const Matrix &b) const
        {
            Matrix ans(n, b.m);
            for (int i = 0; i < n; i++)
                for (int k = 0; k < m; k++)
                    for (int j = 0; j < b.n; j++)
                        ans.data[i][j] = (ans.data[i][j] + (ll)data[i][k] * b.data[k][j]) % P;
            return ans;
        }
    }trans[N];
    namespace Segment_Tree
    {
        struct node
        {
            int f0, f1, f2, tag, sumf0; 
        }tree[N << 2];
        void update(const int rot)
        {
            tree[rot].sumf0 = tree[rot].f0;
            if ((rot << 1 | 1) < (N << 2))
                tree[rot].sumf0 = ((ll)tree[rot].sumf0 + tree[rot << 1].sumf0 + tree[rot << 1 | 1].sumf0) % P;
        }
        void pushdown(const int rot)
        {
            if (tree[rot].tag)
            {
                Matrix tmp(1, 2);
                tree[rot << 1].tag += tree[rot].tag;
                tmp.data[0][0] = tree[rot << 1].f1, tmp.data[0][1] = tree[rot << 1].f2;
                tmp = tmp * trans[tree[rot].tag];
                tree[rot << 1].f1 = tmp.data[0][0], tree[rot << 1].f2 = tmp.data[0][1];
                tree[rot << 1 | 1].tag += tree[rot].tag;
                tmp.data[0][0] = tree[rot << 1 | 1].f1, tmp.data[0][1] = tree[rot << 1 | 1].f2;
                tmp = tmp * trans[tree[rot].tag];
                tree[rot << 1 | 1].f1 = tmp.data[0][0], tree[rot << 1 | 1].f2 = tmp.data[0][1];
                tree[rot].tag = 0;
            }
        }
        void build(const int rot, const int lt, const int rt)
        {
            tree[rot].f1 = 1;
            tree[rot].f0 = tree[rot].f2 = tree[rot].tag = tree[rot].sumf0 = 0;
            if (lt == rt)
                return;
            int mid = (lt + rt) >> 1;
            build(rot << 1, lt, mid), build(rot << 1 | 1, mid + 1, rt);
        }
        void mdf(const int rot, const int lt, const int rt, const int ls, const int rs)
        {
            if (ls <= lt && rt <= rs)
            {
                int f0 = tree[rot].f0, f1 = tree[rot].f1, f2 = tree[rot].f2;
                tree[rot].f0 = ((ll)f0 + (ll)f1 * inv2 + (ll)f2 * inv2) % P;
                tree[rot].f1 = (ll)f1 * inv2 % P;
                tree[rot].f2 = (ll)f2 * inv2 % P;
                ++tree[rot].tag;
                update(rot);
                return;
            }
            int f0 = tree[rot].f0, f1 = tree[rot].f1, f2 = tree[rot].f2;
            tree[rot].f0 = (ll)f0 * inv2 % P;
            tree[rot].f1 = ((ll)f0 * inv2 + f1 + (ll)f2 * inv2) % P;
            tree[rot].f2 = (ll)f2 * inv2 % P;
            pushdown(rot);
            int mid = (lt + rt) >> 1;
            if (rs <= mid)
            {
                int f0 = tree[rot << 1 | 1].f0, f1 = tree[rot << 1 | 1].f1, f2 = tree[rot << 1 | 1].f2;
                tree[rot << 1 | 1].f0 = (f0 + (ll)f2 * inv2) % P;
                tree[rot << 1 | 1].f1 = f1;
                tree[rot << 1 | 1].f2 = (ll)f2 * inv2 % P;
                update(rot << 1 | 1);
                mdf(rot << 1, lt, mid, ls, rs);
            }
            else if (ls > mid)
            {
                int f0 = tree[rot << 1].f0, f1 = tree[rot << 1].f1, f2 = tree[rot << 1].f2;
                tree[rot << 1].f0 = (f0 + (ll)f2 * inv2) % P;
                tree[rot << 1].f1 = f1;
                tree[rot << 1].f2 = (ll)f2 * inv2 % P;
                update(rot << 1);
                mdf(rot << 1 | 1, mid + 1, rt, ls, rs);
            }
            else
            {
                mdf(rot << 1, lt, mid, ls, rs);
                mdf(rot << 1 | 1, mid + 1, rt, ls, rs);
            }
            update(rot);
        }
    }
    int work()
    {
        using namespace Segment_Tree;
        int n, m, tmp = 1;
        inv2 = inv(2);
        read(n), read(m);
        Matrix A(2, 2);
        A.data[0][0] = A.data[0][1] = inv2;
        A.data[1][1] = 1;
        trans[0] = Matrix(2, 2);
        trans[0].data[0][0] = trans[0].data[1][1] = 1;
        for (int i = 1; i <= m; i++)
            trans[i] = trans[i - 1] * A;
        Segment_Tree::build(1, 1, n);
        while (m--)
        {
            int opt;
            read(opt);
            if (opt == 1)
            {
                int l, r;
                read(l), read(r);
                Segment_Tree::mdf(1, 1, n, l, r);
                tmp = tmp * 2LL % P;
            }
            else
                write((ll)Segment_Tree::tree[1].sumf0 * tmp % P), putchar('\n');
        }
        return 0;
    }
}
int main()
{
    return zyt::work();
}

猜你喜欢

转载自www.cnblogs.com/zyt1253679098/p/12395541.html
今日推荐