「ZJOI2019」线段树-线段树

Description

这是链接

Solution

这题可以看做每次有 1 2 \frac {1} {2} 21执行1操作,问 t a g tag tag数组之和的期望乘 2 p 2^p 2p p p p1操作的次数)。

根据期望的线性性,单独计算出每一个节点的 t a g tag tag 1 1 1的期望 f i f_i fi,顺便计 g i g_i gi表示 i i i到根的路径有 t a g = 1 tag=1 tag=1的概率。将 [ l , r ] [l,r] [l,r]拆分成 l o g log log个在线段树上的区间,考虑每一个区间对答案的影响,可以发现对于这个区间到根的路径上的点(不包括它本身)的 f i ← f i 2 , g i ← g i 2 f_i \gets \frac{f_i}{2},g_i \gets \frac{g_i}{2} fi2fi,gi2gi,临近这条路径的点的 f i ← f i + g i 2 , g i f_i \gets \frac{f_i+g_i}{2},g_i fi2fi+gi,gi不变,而这个区间本身 f i ← f i + 1 2 f_i \gets \frac {f_i+1}{2} fi2fi+1,其子树的 g i ← g i + 1 2 g_i \gets \frac{g_i+1}{2} gi2gi+1

这里的修改都是 O ( l o g ) O(log) O(log)级别,暴力修改即可。对于 g i g_i gi的维护,可以维护 g i ′ = 1 − g i g_i'=1-g_i gi=1gi,修改时 g i ′ ← = g i ′ 2 g_i'\gets=\frac{g_i'}{2} gi=2gi

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

inline int gi()
{
	char c = getchar();
	while(c < '0' || c > '9') c = getchar();
	int sum = 0;
	while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
	return sum;
}

typedef long long ll;
const int maxn = 100005, mod = 998244353;

int n, m, P = 1, inv[maxn], f[maxn << 2], g[maxn << 2], lzy[maxn << 2], ans;

#define mid ((l + r) >> 1)
#define lch (s << 1)
#define rch (s << 1 | 1)

void build(int s, int l, int r)
{
	g[s] = 1;
	if (l == r) return ;
	build(lch, l, mid);
	build(rch, mid + 1, r);
}

inline void mdf_f(int x, int v) {ans = (ans - f[x] + mod) % mod; f[x] = (ll)(f[x] + v) * inv[1] % mod; ans = (ans + f[x]) % mod;}
inline void mdf_g(int x, int v) {g[x] = (ll)g[x] * inv[v] % mod; lzy[x] += v;}
inline void push_down(int s)
{
	if (!lzy[s]) return ;
	mdf_g(lch, lzy[s]); mdf_g(rch, lzy[s]);
	lzy[s] = 0;
}

void modify(int s, int l, int r, int ql, int qr)
{
	if (ql <= l && r <= qr) return mdf_f(s, 1), mdf_g(s, 1), void();
	push_down(s);
	if (ql <= mid) modify(lch, l, mid, ql, qr);
	else mdf_f(lch, mod + 1 - g[lch]);
	if (mid < qr) modify(rch, mid + 1, r, ql, qr);
	else mdf_f(rch, mod + 1 - g[rch]);
	mdf_f(s, 0); g[s] = (ll)(g[s] + 1) * inv[1] % mod;
}

int main()
{
	n = gi(); m = gi();
	inv[0] = 1; inv[1] = (mod + 1) >> 1;
	for (int i = 2; i <= n; ++i) inv[i] = (ll)inv[i - 1] * inv[1] % mod;
	
	build(1, 1, n);
	for (int op, l, r, i = 1; i <= m; ++i) {
		op = gi(); 
		if (op == 1) l = gi(), r = gi(), P = (P << 1) % mod, modify(1, 1, n, l, r);
		else printf("%lld\n", (ll)ans * P % mod);
	}
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值