P1471 方差
Tooler_Yang
·
·
个人记录
这道题其实可以用树状数组来做。(线段树太板了,没有思维含量)
树状数组是用于单点修改,区间查询,或者区间修改,单点查询。 而这道题目是区间修改,区间查询。
所以我们要使用差分来转化。
例如 a:\{1,3,4,3,5\},那么它的差分序列就是 b:\{1,2,1,-1,2\}。
实现方式就是 b_i=a_i-a_{i-1},如果有差分数组 b,那么有 a_i=\sum\limits_{j=1}^{i}b_j
那有什么好处?
还是那个 a 数组,假如需要将 [2,4] 之间的数都增加 2,那相应的,可以将 b 数组中 b_1 的值减 1,再把 b_4 的值加 1,就可以达到同样的效果。
那么如果在 a 数组中不管改变长度为多少的区间,在差分数组 b 中只需要更新两个数即可。
代码如下:
int lowbit(int x){return x&(-x);}
void add(int p,int x){
while(p<=n){
d[p]+=x;
p+=lowbit(p);
}
}
int sum(int x){
long long ans=0;
while(x){
ans+=d[x];
x-=lowbit(x);
}
return ans;
}
int main(){
int m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>b[i];
}
for(int i=1;i<=m;i++){
int a;
cin>>a;
if(a==1){
int b,c,d;
cin>>b>>c>>d;
add(b,d);
add(c+1,-d);
}
else if(a==2){
int x;
cin>>x;
cout<<b[x]+sum(x)<<"\n";
}
}
但是这只是区间查询,单点查找的代码,那如何查询区间呢?
那就一个一个查呗!
假设差分数组为 b,要求 a 数组在区间 [l,r] 的和,那所求的式子:
\begin{aligned}
\sum_{i=l}^{r}a_i
&=\sum_{i=l}^{r}\sum_{j=1}^{i}b_j\\
&=(\sum_{i=1}^{r}\sum_{j=1}^{i}b_j)-(\sum_{i=1}^{l-1}\sum_{j=1}^{i}b_j)\\
&=(\sum_{i=1}^{r}(r-i+1)\cdot b_i)-(\sum_{i=1}^{l-1}(l-i)\cdot b_i)\\
&=((r+1)\sum_{i=1}^{r}b_i-\sum_{i=1}^{r}i\cdot b_i)-(l\cdot\sum_{i=1}^{l-1}b_i-\sum_{i=1}^{l-1}i\cdot b_i)
\end{aligned}
然而 b_i,i\cdot b_i 都是能通过树状数组来维护的。
代码如下:
int lowbit(int x){return x&(-x);}
void add(int p,int x){
int u=p;
while(p<=n){
d[p]+=x;
di[p]+=x*(u-1);
p+=lowbit(p);
}
}
int sum(int x){
long long ans=0;
int p=x;
while(x){
ans+=p*d[x]-di[x];
x-=lowbit(x);
}
return ans;
}
int get_sum(int l,int r){
return sum(r)-sum(l-1);
}
那么在区间 [l,r] 之间的平均数就只要再除以 (r-l+1) 即可。
设区间 [l,r] 之间的平均数为 \bar{a},区间长 r-l+1=n。那方差就是:
\begin{aligned}
s^2&=\frac{1}{n}\sum_{i=l}^{r}(a_i-\bar{a})^2\\
&=\frac{1}{n}(\sum_{i=l}^{r}a_i^2-2\bar{a}\cdot\sum_{i=l}^{r}a_i+n\cdot\bar{a}^2)\\
&=\frac{1}{n}(\sum_{i=l}^{r}a_i^2-\frac{2}{n}(\sum_{i=l}^{r}a_i)^2+\frac{n}{n^2}(\sum_{i=l}^{r}a_i)^2)\\
&=\frac{1}{n}(\sum_{i=l}^{r}a_i^2-\frac{1}{n}(\sum_{i=l}^{r}a_i)^2)\\
&=\frac{1}{n}\sum_{i=l}^{r}a_i^2-\frac{1}{n^2}(\sum_{i=l}^{r}a_i)^2
\end{aligned}
只要再直接用树状数组维护 a_i^2 即可。
再设 \sum\limits_{i=1}^{k} a_i=S_k,运用阿贝尔变换
\begin{aligned}
\sum_{i=l}^{r}a_i^2
&=\sum_{i=l}^{r}a_i\cdot a_i\\
&=(S_r-S_{l-1})a_r+\sum\limits_{k=l}^{r-1}(S_k-S_{l-1})(a_k-a_{k+1})\\
&=(S_r-S_{l-1})a_r+\sum\limits_{k=l}^{r-1}\sum\limits_{p=l}^{p}a_p(a_p-a_{p+1})\\
&=(S_r-S_{l-1})a_r+\sum\limits_{k=l}^{r-1}(r-i)(a_k-a_{k+1})a_k\\
&=a_r\sum\limits_{i=l}^{r}a_i+\sum\limits_{k=l}^{r-1}(r-i)(a_k-a_{k+1})a_k\\
&=a_r\sum\limits_{i=l}^{r}a_i+\sum\limits_{k=l}^{r-1}(a_k-a_{k+1})ra_k-\sum\limits_{k=l}^{r-1}(a_k-a_{k+1})ia_k\\
&=a_r\sum\limits_{i=l}^{r}a_i+r\sum\limits_{k=l}^{r-1}(a_k-a_{k+1})a_k-\sum\limits_{k=l}^{r-1}(a_k-a_{k+1})ia_k
\end{aligned}
然后就只要维护 (a_k-a_{k+1})a_k 就可以了,((a_k-a_{k+1})ia_k 同理),就会发现 (a_k-a_{k+1}) 就是差分。
可是差分涉及到了两个值 a_k 和 a_{k+1},所以如果要修改时就要分两种情况,同时设 (a_k-a_{k+1})a_k=A_k:
其实就是要实现一个数组 P 使得 P_i=\sum\limits_{j=1}^{i}A_j
则有 P 的树状数组:
\begin{aligned}
Ptree_1&=A_1\\
Ptree_2&=2A_1+A_2\\
Ptree_3&=A_1+A_2+A_3\\
Ptree_4&=4A_1+2A_2+A_3+A_4\\
Ptree_5&=A_1+A_2+A_3+A_4+A_5\\
Ptree_6&=2A_1+2A_2+2A_3+A_4+A_5+A_6\\
Ptree_7&=A_1+A_2+A_3+A_4+A_5+A_6+A_7\\
Ptree_8&=8A_1+4A_2+2A_3+2A_4+A_5+A_6+A_7+A_8\\
\end{aligned}
只能说毫无规律(有可能算错)