[unknown]切面包

137 篇文章 1 订阅
11 篇文章 0 订阅

出题者 j i a n g l y \sf jiangly jiangly,爆零者 O n e I n D a r k \sf OneInDark OneInDark

题目

题目描述
H H H 有条一长长的面包。这条面包由 n n n 段组成。

每当有朋友到小 H H H 的家里玩,小 H H H 就会切下这块面包的一段,分享给朋友们。之后,小 H H H 又会重新制作和之前完全一样的若干段面包填充切下的部分。

面包被切下时,有可能会裂开,第 i i i 段面包被切下时裂开的概率为 p i 998244352 p_i\over 998244352 998244352pi 。小 H H H 想让朋友尽可能开心,于是会把裂开的面包自己吃掉,而用没有裂开的面包招待朋友。

H H H 特别喜欢分块,因此,如果小 H H H x x x 段连续的没有裂开的面包,则朋友的开心度为 x 2 x^2 x2

有时,小 H H H 也会对一段面包进行加工,加工会改变面包裂开的概率。

你需要在每次小 H H H 切蛋糕之前,回答他朋友的期望开心度是多少。

输入格式
第一行两个整数 n , m    ( 1 ≤ n , m ≤ 1 0 5 ) n,m\;(1 ≤ n, m ≤ 10^5) n,m(1n,m105) ,分别表示面包的长度和时间的次数。

第二行 n n n 个整数 p 1 , p 2 , … , p n    ( 0 ≤ p i ≤ 998244352 ) p_1, p_2,\dots, p_n\;(0 ≤ p_i ≤ 998244352) p1,p2,,pn(0pi998244352) ,表示每段面包初始时裂开的概率。

接下来 m m m 行,每行表示一个事件:

  • 1   x i   q i    ( 1 ≤ x i ≤ n , 0 ≤ q i ≤ 998244352 ) 1\space x_i\space q_i\; (1 ≤ x_i ≤ n, 0 ≤ q_i ≤ 998244352) 1 xi qi(1xin,0qi998244352)
    表示小 H H H 加工了第 x i x_i xi 段面包,加工后 p x i p_{x_i} pxi 变成了 q i q_i qi

  • 2   l i   r i    ( 1 ≤ l i ≤ r i ≤ n ) 2\space l_i\space r_i\; (1 ≤ l_i ≤ r_i ≤ n) 2 li ri(1lirin)
    表示小 H H H 询问若切下 [ l i , r i ] [l_i, r_i] [li,ri] 内的面包,朋友的期望开心度是多少。

思路壹

当时我一个大力展开 ( x + y ) 2 = x 2 + 2 x y + y 2 (x+y)^2=x^2+2xy+y^2 (x+y)2=x2+2xy+y2,直接套期望,然后 W A \tt{WA} WA 穿……

首先定义 E S = ∑ x ∈ S ∣ x ∣ ⋅ P ( x ) E_{S}=\sum_{x\in S}|x|\cdot P(x) ES=xSxP(x),其中 x x x 是某个局面(情况),其权值为 ∣ x ∣ |x| x,而 P ( x ) P(x) P(x) 是情况 x x x 发生的概率。同理定义 P ( S ) = ∑ x ∈ S P ( x ) P(S)=\sum_{x\in S}P(x) P(S)=xSP(x)

考虑线段树上的区间合并,即 z = x + y z=x+y z=x+y(表示 x , y x,y x,y 同时发生),那么 ∣ z ∣ = ∣ x ∣ + ∣ y ∣ ,    P ( z ) = P ( x ) P ( y ) |z|=|x|+|y|,\;P(z)=P(x)P(y) z=x+y,P(z)=P(x)P(y)

假设左儿子有 x x x 段,右儿子有 y y y 段,那么 z = x + y z=x+y z=x+y 的期望是多少呢?

  • 情况一:中间没有连起来

E S z = ∑ z ∈ S z ∣ z ∣ ⋅ P ( z ) = ∑ x ∈ S x ,    y ∈ S y ( ∣ x ∣ + ∣ y ∣ ) ⋅ P ( x ) P ( y ) = ∑ x ∈ S x ∣ x ∣ ⋅ E ( x ) P ( S y ) + E ( x ) E P y = E p x E ( P y ) + E ( P x ) E p y \begin{aligned} E_{S_z}&=\sum_{z\in S_z}|z|\cdot P(z)\\ &=\sum_{x\in S_x,\;y\in S_y}(|x|+|y|)\cdot P(x)P(y)\\ &=\sum_{x\in S_x}|x|\cdot E(x)P(S_y)+E(x)E_{P_y}\\ &=E_{p_x}E(P_y)+E(P_x)E_{p_y} \end{aligned} ESz=zSzzP(z)=xSx,ySy(x+y)P(x)P(y)=xSxxE(x)P(Sy)+E(x)EPy=EpxE(Py)+E(Px)Epy

  • 情况二:中间连起来了

E P z = ∑ x ∈ P x , y ∈ P y ( x + y − 1 ) E ( x ) E ( y ) = ∑ x ∈ P x x E ( x ) E ( P y ) + E ( x ) E P y − E ( x ) E ( P y ) = E p x E ( P y ) + E ( P x ) E p y − E ( P x ) E ( P y ) \begin{aligned} E_{P_z}&=\sum_{x\in P_x,y\in P_y}(x+y-1)E(x)E(y)\\ &=\sum_{x\in P_x}xE(x)E(P_y)+E(x)E_{P_y}-E(x)E(P_y)\\ &=E_{p_x}E(P_y)+E(P_x)E_{p_y}-E(P_x)E(P_y) \end{aligned} EPz=xPx,yPy(x+y1)E(x)E(y)=xPxxE(x)E(Py)+E(x)EPyE(x)E(Py)=EpxE(Py)+E(Px)EpyE(Px)E(Py)

好,搞定。接下来考虑二次项(用 E P x 2 E_{P_{x2}} EPx2 表示):

  • 情况一:中间没有连起来

E P z = ∑ x ∈ P x , y ∈ P y ( x + y ) 2 E ( x ) E ( y ) = ∑ x ∈ P x x 2 E ( x ) E ( P y ) + 2 x E ( x ) E P y + E ( x ) E P y 2 = E P x 2 E ( P y ) + 2 E P x E P y + E ( P x ) E P y 2 \begin{aligned} E_{P_z}&=\sum_{x\in P_x,y\in P_y}(x+y)^2E(x)E(y)\\ &=\sum_{x\in P_x}x^2E(x)E(P_y)+2xE(x)E_{P_y}+E(x)E_{P_{y2}}\\ &=E_{P_{x2}}E(P_y)+2E_{P_x}E_{P_y}+E(P_x)E_{P_{y2}} \end{aligned} EPz=xPx,yPy(x+y)2E(x)E(y)=xPxx2E(x)E(Py)+2xE(x)EPy+E(x)EPy2=EPx2E(Py)+2EPxEPy+E(Px)EPy2

  • 情况二:中间连起来了

由于 ( x + y − 1 ) 2 = ( x + y ) 2 − 2 ( x + y ) + 1 (x+y-1)^2=(x+y)^2-2(x+y)+1 (x+y1)2=(x+y)22(x+y)+1 ,第一项普通平方也已经搞定了,所以只考虑后面那一大坨。

∑ x ∈ P x , y ∈ P y − 2 ( x + y ) E ( x ) E ( y ) + E ( x ) E ( y ) = ∑ x ∈ P x − 2 x E ( x ) E ( P y ) − 2 E ( x ) E P y + E ( x ) E ( P y ) = − 2 E P x E ( P y ) − 2 E ( P x ) E P y + E ( P x ) E ( P y ) \begin{aligned} &\sum_{x\in P_x,y\in P_y}-2(x+y)E(x)E(y)+E(x)E(y)\\ &=\sum_{x\in P_x}-2xE(x)E(P_y)-2E(x)E_{P_{y}}+E(x)E(P_y)\\ &=-2E_{P_{x}}E(P_y)-2E(P_x)E_{P_{y}}+E(P_x)E(P_y) \end{aligned} xPx,yPy2(x+y)E(x)E(y)+E(x)E(y)=xPx2xE(x)E(Py)2E(x)EPy+E(x)E(Py)=2EPxE(Py)2E(Px)EPy+E(Px)E(Py)

所以用线段树维护 左右端点面包是否裂开(本质是把 x x x 划分成 P 0 , P 1 , P 2 , P 3 P_0,P_1,P_2,P_3 P0,P1,P2,P3 四种类别)的一次、二次期望。

没了。就是常数有点大, 1 s \tt 1s 1s 跑不过,要 2 s \tt 2s 2s ,而且细节多……总之是道“想到了打不了”的题。

代码壹

#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
inline int readint(){
	int a = 0, f = 1; char c = getchar();
	for(; c<'0' or c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c and c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MaxN = 100001, Mod = 998244353;
int pro[MaxN], n, m; // 存在的概率 

class SegmentTree{
	struct Node{
		int l, r;
		int data[4][2];
		Node(){
			for(int i=0; i<4; ++i)
				for(int j=0; j<2; ++j)
					data[i][j] = 0;
		}
	}node[MaxN<<2];
	void pushUp(const Node &p1,const Node &p2,Node &p){
		for(int i=0; i<4; ++i)
			for(int j=0; j<2; ++j)
				p.data[i][j] = 0;
		for(int i=0; i<4; ++i)
			for(int j=0; j<4; ++j){
				if(p1.l == p1.r and i != 3 and i != 0)
					continue;
				if(p2.l == p2.r and j != 3 and j != 0)
					continue;
				int d = (i&1)|(j&2);

				int ex = 1, ey = 1;
				if(i&1) ex = 1ll*ex*pro[p1.l]%Mod;
				else ex = ex*(Mod+1ll-pro[p1.l])%Mod;
				if(p1.l != p1.r){
					if(i&2) ex = 1ll*ex*pro[p1.r]%Mod;
					else ex = (Mod+1ll-pro[p1.r])*ex%Mod;
				}
				if(j&1) ey = 1ll*ey*pro[p2.l]%Mod;
				else ey = ey*(Mod+1ll-pro[p2.l])%Mod;
				if(p2.l != p2.r){
					if(j&2) ey = 1ll*ey*pro[p2.r]%Mod;
					else ey = (Mod+1ll-pro[p2.r])*ey%Mod;
				}

				p.data[d][0] = (p.data[d][0]+1ll*p1.data[i][0]*ey%Mod+1ll*p2.data[j][0]*ex%Mod)%Mod;
				if((i&2) and (j&1)) p.data[d][0] = (p.data[d][0]-1ll*ex*ey%Mod+Mod)%Mod;

				p.data[d][1] = (p.data[d][1]+1ll*p1.data[i][1]*ey%Mod+2ll*p1.data[i][0]*p2.data[j][0]%Mod+1ll*ex*p2.data[j][1]%Mod)%Mod;
				if((i&2) and (j&1)) p.data[d][1] = (p.data[d][1]-2ll*p1.data[i][0]*ey%Mod-2ll*ex*p2.data[j][0]%Mod+1ll*ex*ey%Mod+(Mod<<1))%Mod;
			}
	}
	void pushUp(int pos){
		return pushUp(node[pos<<1],node[pos<<1|1],node[pos]);
	}
public:
	void buildTree(int l=1,int r=n,int pos=1){
		node[pos].l = l, node[pos].r = r;
		if(l != r){
			buildTree(l,(l+r)>>1,pos<<1);
			buildTree((l+r)/2+1,r,pos<<1|1);
			pushUp(pos);
		}
		else{
			pro[l] = (Mod+1ll+pro[l])%Mod;
			node[pos].data[3][0] = pro[l];
			node[pos].data[3][1] = pro[l];
		}
	}
	void modifyPoint(int id,int v,int pos=1){
		if(node[pos].l == node[pos].r){
			pro[id] = (Mod+1ll+v)%Mod;
			node[pos].data[3][0] = pro[id];
			node[pos].data[3][1] = pro[id];
			return ;
		}
		int mid = (node[pos].l+node[pos].r)>>1;
		if(id <= mid)
			modifyPoint(id,v,pos<<1);
		else
			modifyPoint(id,v,pos<<1|1);
		pushUp(pos);
	}
	Node QueryNode(int l,int r,int pos){
		if(l <= node[pos].l and node[pos].r <= r)
			return node[pos];
		int mid = (node[pos].l+node[pos].r)>>1;
		if(r <= mid)
			return QueryNode(l,r,pos<<1);
		if(l > mid)
			return QueryNode(l,r,pos<<1|1);
		Node a = QueryNode(l,r,pos<<1),
			b = QueryNode(l,r,pos<<1|1);
		Node c; pushUp(a,b,c);
		c.l = a.l, c.r = b.r;
		return c;
	}
	int Query(int l,int r){
		Node ans = QueryNode(l,r,1);
		int res = 0;
		for(int i=0; i<4; ++i)
			res = (0ll+res+ans.data[i][1])%Mod;
		return res;
	}
	void printNode(int pos){
		printf("node[%d]:\n",pos);
		for(int i=0; i<4; ++i)
			for(int j=0; j<2; ++j)
				printf("data[%d %d]: %d\n",i,j,node[pos].data[i][j]);
	}
	void monitor(){
		for(int i=1; i<=5; ++i)
			printNode(i);
	}
}ppl;

int main(){
	// freopen("divide.in","r",stdin);
	// freopen("divide.out","w",stdout);
	n = readint(), m = readint();
	for(int i=1; i<=n; ++i)
		pro[i] = readint();
	ppl.buildTree();
	// ppl.monitor();
	for(int i=1; i<=m; ++i){
		int cmd = readint();
		int one = readint();
		int two = readint();
		if(cmd == 1)
			ppl.modifyPoint(one,two);
		else
			printf("%d\n",ppl.Query(one,two));
	}
	return 0;
}

思路贰

自己感受题解做法……而且有个系数写错了(已用红笔标注)……可见恶心之处……
在这里插入图片描述

代码贰

你希望我写这个玩意儿???

“喂,妖妖灵吗?有人想谋财害命!”
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值