• 【洛谷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}) ,一次修改可以转化为一次矩阵乘法:

    [egin{bmatrix}f_{u,1}&f_{u,2}end{bmatrix} egin{bmatrix}1&0\frac{1}{2}&frac{1}{2}end{bmatrix}= egin{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('
    ');
    		}
    		return 0;
    	}
    }
    int main()
    {
    	return zyt::work();
    }
    
  • 相关阅读:
    Keras实例教程(2)
    Keras实例教程(1)
    tf.nn.l2_loss()的用法
    在tensorflow中使用batch normalization
    Tensorflow的LRN是怎么做的
    CNN卷积中多通道卷积的参数问题
    caffe学习网站
    交叉熵反向求导计算过程
    矩阵求导
    循环神经网络(RNN)模型与前向反向传播算法
  • 原文地址:https://www.cnblogs.com/zyt1253679098/p/12395541.html
Copyright © 2020-2023  润新知