【洛谷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();
}
posted @ 2020-03-02 14:36  Inspector_Javert  阅读(116)  评论(0编辑  收藏  举报