1. 程式人生 > >[2018.10.18 T3] 小 G 的線段樹

[2018.10.18 T3] 小 G 的線段樹

暫無連結

小 G 的線段樹

【題目描述】

小 G 是一名 O I e r OIer ,他最近學習了一種高階資料結構——線段樹,
做題時,他遇到了如下的問題:
維護一個序列,要求支援三種操作:
1.區間加上一個數 x

x
2.區間賦值為一個數 x x
3.求一個區間的和
小 G 是一個愛思考的同學。他在做出來了這題之後,又提出了一個新的問題:如果把所有的操作隨機打亂,那麼每個詢問的期望輸出是多少呢?注意,隨機打亂既所有 m !
m!
種操作排列的出現概率均等。為了方便,我們假設詢問在最後且不參與隨機打亂。
考慮到精度問題,只要你的答案和標程答案的相對誤差不超過 1 e 8 1e-8
就算正確。

【輸入格式】

第一行三個整數 n   m   q n\ m\ q ,分別表示序列長度、修改數和詢問數接下來一行 n n 個整數 a i a_i ,表示序列的初始值接下來 m m 行,每行 4 4 個整數 c , l , r , x c,l,r,x
c = 1 c=1 ,則表示把區間 [ l , r ] [l,r] 的元素加上 x x
c = 2 c=2 ,則表示把區間 [ l , r ] [l,r] 的元素全賦為 x x
接下來 q q 行,每行 2 2 個整數 l , r l,r ,代表每次詢問的左右端點。

【輸出格式】

q q 行,每行一個實數,按照輸入順序分別為 q q 個詢問的期望答案
答案保留 3 位小數

【樣例 1】
segment. in

5 4 8
2 3 3 3 3
1 1 3 2
1 3 5 1
2 2 4 1
2 1 3 4
1 1
2 2
3 3
4 4
5 5
1 3
2 5
1 5

segment.out

5.000
3.167
3.500
1.500
4.000
11.667
12.167
17.167

【樣例 2】

見選手目錄下 segment. in/segment.ans

【資料範圍與約定】

對於 30 % 30\% 的資料, n < = 10 , m < = 10 n<=10,m<=10
對於 60 % 60\% 的資料, n < = 1000 , m < = 1000 n<=1000,m<=1000
對於另外 10 % 10\% 的資料,沒有操作 1 1
對於另外 10 % 10\% 的資料, q = 1 q=1
對於 100 % 100\% 的資料, n , m , q < = 100000 1 < = a i < = 100000 n,m,q<=100000,1<=a_i<=100000 1 1 操作中的
x < = 100 x<=100 2 2 操作中的 x < = 100000 x<=100000

【提示】

離散型隨機變數的一切可能取值 x i x_i 與其對應的概率 p i p_i 的乘積之和稱為該離散型隨機變數的期望,即 E ( x ) = i = 1 n x i p i E(x)=\sum_{i=1}^nx_ip_i

題解

先考慮如何計算單點的答案:

首先,該點的初始值只在無賦值操作的時候有效;考慮 n n 個賦值操作將整個操作序列分成了 n + 1 n+1 份,只有最後一個賦值操作有效,所以賦值操作對答案的貢獻為 x n \frac{x}{n} ;只有在最後一個賦值操作之後的加操作有效,所以加操作對答案的貢獻為 x n + 1 \frac{x}{n+1}

我們只需要查分統計每個點的賦值操作個數、賦值與加操作權值即可。

程式碼
#include<cstdio>
const int M=1e5+5;
int tot1,tot2,n,m,q,que[M];
long long add[M],ass[M],cot[M];
double ans[M];
void in(){scanf("%d%d%d",&n,&m,&q);for(int i=1;i<=n;++i)scanf("%d",&que[i]);}
void ac()
{
	for(int i=1,op,l,r,x;i<=m;++i)
	{
		scanf("%d%d%d%d",&op,&l,&r,&x);
		op==1?(add[l]+=x,add[r+1]-=x):(ass[l]+=x,ass[r+1]-=x,++cot[l],--cot[r+1]);
	}
	for(int i=1;i<=n;++i)add[i]+=add[i-1],ass[i]+=ass[i-1],cot[i]+=cot[i-1],ans[i]=add[i]/(cot[i]+1.0);
	for(int i=1;i<=n;++i)ans[i]+=(cot[i]?1.0*ass[i]/cot[i]:que[i]);
	for(int i=1;i<=n;++i)ans[i]+=ans[i-1];
	for(int i=1,l,r;i<=q;++i)
	scanf("%d%d",&l,&r),printf("%.3lf\n",ans[r]-ans[l-1]);
}
int main(){in(),ac();}