序 列 方 差 序列方差 序列方差
正 解 部 分 \color{red}{正解部分} 正解部分
考虑一个长度为 m m m 的 子序列 对答案的贡献,
1 m ∑ i = 1 m ( a i − a ‾ ) 2 = 1 m ( ∑ i = 1 m a i 2 + ∑ i = 1 m a ‾ 2 − 2 ∑ i = 1 m a i a ‾ ) = 1 m ( ∑ i = 1 m a i 2 + m ( ∑ i = 1 m a i ) 2 m 2 − 2 ∑ i = 1 m a i ∑ j = 1 m a j m ) = 1 m ( ∑ i = 1 m a i 2 + 1 m ( ∑ i = 1 m a i ) 2 − 2 m ( ∑ i = 1 m a i ) 2 ) = 1 m ( ∑ i = 1 m a i 2 − 1 m ( ∑ i = 1 m a i ) 2 ) = 1 m ∑ i = 1 m a i 2 − 1 m 2 ( ∑ i = 1 m a i ) 2 \begin{aligned} & \frac{1}{m}\sum_{i=1}^m (a_i - \overline{a})^2 \\ & = \frac{1}{m} \left( \sum_{i=1}^m a_i^2 + \sum_{i=1}^m \overline{a}^2 - 2 \sum_{i=1}^ma_i\overline{a} \right)\\ & = \frac{1}{m} \left( \sum_{i=1}^ma_i^2 + m \frac{\left(\sum_{i=1}^m a_i\right)^2}{m^2} - 2 \sum_{i=1}^m a_i \frac{\sum_{j=1}^m a_j}{m}\right)\\ & = \frac{1}{m} \left( \sum_{i=1}^ma_i^2 + \frac{1}{m}\left(\sum_{i=1}^ma_i \right)^2 -\frac{2}{m}\left(\sum_{i=1}^ma_i \right)^2 \right)\\ & = \frac{1}{m} \left( \sum_{i=1}^ma_i^2 - \frac{1}{m}\left(\sum_{i=1}^ma_i \right)^2 \right) \\ & = \frac{1}{m} \sum_{i=1}^ma_i^2 - \frac{1}{m^2}\left(\sum_{i=1}^ma_i \right)^2 \end{aligned} m1i=1∑m(ai−a)2=m1(i=1∑mai2+i=1∑ma2−2i=1∑maia)=m1(i=1∑mai2+mm2(∑i=1mai)2−2i=1∑maim∑j=1maj)=m1⎝⎛i=1∑mai2+m1(i=1∑mai)2−m2(i=1∑mai)2⎠⎞=m1⎝⎛i=1∑mai2−m1(i=1∑mai)2⎠⎞=m1i=1∑mai2−m21(i=1∑mai)2
但是现在要计算一个序列中的 所有子序列 的 贡献和, 假设当前询问区间是 [ 1 , n ] [1, n] [1,n],
对一个数字 a i a_i ai 单独考虑其对答案的贡献, 1 m ∑ i = 1 m a i 2 − 1 m 2 ( ∑ i = 1 m a i ) 2 \frac{1}{m} \sum\limits_{i=1}^ma_i^2 - \frac{1}{m^2}\left(\sum\limits_{i=1}^ma_i \right)^2 m1i=1∑mai2−m21(i=1∑mai)2,
先计算这个式子左项, 为 1 m ( n − 1 m − 1 ) ∑ i = 1 n a i 2 \frac{1}{m} \begin{pmatrix} n-1 \\ m-1 \end{pmatrix} \sum\limits_{i=1}^n a_i^2 m1(n−1m−1)i=1∑nai2,
再考虑这个式子右项, 为
−
1
m
2
{
(
n
−
1
m
−
1
)
∑
i
=
1
n
a
i
2
+
(
n
−
2
m
−
2
)
∑
i
=
1
n
a
i
[
(
∑
j
=
1
n
a
j
)
−
a
i
]
}
-\frac{1}{m^2} \left\{ \begin{pmatrix} n-1 \\ m-1 \end{pmatrix} \sum\limits_{i=1}^n a_i^2 + \begin{pmatrix} n-2 \\ m-2 \end{pmatrix} \sum\limits_{i=1}^n a_i\left[ (\sum\limits_{j=1}^n a_j) - a_i\right] \right\}
−m21{(n−1m−1)i=1∑nai2+(n−2m−2)i=1∑nai[(j=1∑naj)−ai]}
继续化简得到
−
1
m
2
{
(
n
−
1
m
−
1
)
∑
i
=
1
n
a
i
2
+
(
n
−
2
m
−
2
)
[
(
∑
i
=
1
n
a
i
)
2
−
∑
i
=
1
n
a
i
2
]
}
-\frac{1}{m^2} \left\{ \begin{pmatrix} n-1 \\ m-1 \end{pmatrix} \sum\limits_{i=1}^n a_i^2 + \begin{pmatrix} n-2 \\ m-2 \end{pmatrix} \left[\left(\sum\limits_{i=1}^n a_i\right)^2 - \sum\limits_{i=1}^na_i^2\right]\right\}
−m21{(n−1m−1)i=1∑nai2+(n−2m−2)[(i=1∑nai)2−i=1∑nai2]} .
设 f ( n ) = ∑ i = 1 n a i 2 , g ( n ) = ∑ i = 1 n a i f(n) = \sum_{i=1}^n a_i^2, g(n) = \sum_{i=1}^n a_i f(n)=∑i=1nai2,g(n)=∑i=1nai, 合并两项, 得到 总贡献,
( 1 m − 1 m 2 ) ( n − 1 m − 1 ) f ( n ) − 1 m 2 ( n − 2 m − 2 ) [ g ( n ) 2 − f ( n ) ] \left(\frac{1}{m}- \frac{1}{m^2}\right)\begin{pmatrix} n-1 \\ m-1\end{pmatrix} f(n) - \frac{1}{m^2} \begin{pmatrix} n-2 \\ m-2 \end{pmatrix}\left[g(n)^2 - f(n)\right] (m1−m21)(n−1m−1)f(n)−m21(n−2m−2)[g(n)2−f(n)]
将 组合数 展开, 可得
( n − 1 ) ! m 2 ( n − m ) ! ( m − 2 ) ! f ( n ) − ( n − 2 ) ! m 2 ( n − m ) ! ( m − 2 ) ! [ g ( n ) 2 − f ( n ) ] \frac{(n-1)!}{m^2(n-m)!(m-2)!}f(n) - \frac{(n-2)!}{m^2(n-m)!(m-2)!}[g(n)^2 - f(n)] m2(n−m)!(m−2)!(n−1)!f(n)−m2(n−m)!(m−2)!(n−2)![g(n)2−f(n)] .
分母是一样的, 设两个多项式 p 1 ( x ) = x ! p 2 ( x ) = x 2 ( x − 2 ) ! p_1(x) = x!\ \ p_2(x) = x^2(x-2)! p1(x)=x! p2(x)=x2(x−2)!, 则两个多项式的 卷积 的 n n n 次项系数即为 分母 的总和 .
再配合 线段树 计算 f ( n ) , g ( n ) 2 f(n), g(n)^2 f(n),g(n)2 即可 O ( n log n ) O(n\log n) O(nlogn) 解决这道题 .
实 现 部 分 \color{red}{实现部分} 实现部分
#include<bits/stdc++.h>
#define reg register
const int maxn = 1e6 + 5;
const int mod = 998244353;
int read(){
char c;
int s = 0, flag = 1;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ flag = -1, c = getchar(); break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
int N;
int Q_;
int Tmp_1;
int ntt_len;
int p1[maxn];
int p2[maxn];
int fac[maxn];
int rev[maxn];
int Ksm(int a, int b){ int s=1; while(b){ if(b&1) s=1ll*s*a%mod; a=1ll*a*a%mod; b>>=1; } return s; }
void Ntt(int *f, int opt){
for(reg int i = 0; i < ntt_len; i ++) if(i < rev[i]) std::swap(f[i], f[rev[i]]);
for(reg int p = 2; p <= ntt_len; p <<= 1){
int half = p >> 1;
int wn = Ksm(3, (mod-1)/p);
if(opt == -1) wn = Ksm(wn, mod-2);
for(reg int i = 0; i < ntt_len; i += p){
int t = 1;
for(reg int j = i; j < i+half; j ++){
int tmp = 1ll*t*f[j+half] % mod;
f[j+half] = (f[j] - tmp + mod) % mod; f[j] = (f[j] + tmp) % mod;
t = 1ll*t*wn % mod;
}
}
}
}
struct Segment_Tree{
struct Node{ int l, r, s1, s2, tag; } T[maxn << 3];
void Build(int k, int l, int r){
T[k].l = l, T[k].r = r;
if(l == r) return ;
int mid = l+r >> 1;
Build(k<<1, l, mid), Build(k<<1|1, mid+1, r);
}
void Push_down(int k){
int l = T[k].l, r = T[k].r;
T[k].s2 = (T[k].s2 + (2ll*T[k].s1*T[k].tag%mod + (1ll*r-l+1)*T[k].tag%mod*T[k].tag%mod)%mod)%mod;
T[k].s1 = (T[k].s1 + 1ll*(r-l+1)*T[k].tag%mod) % mod;
T[k<<1].tag = (T[k<<1].tag + T[k].tag) % mod, T[k<<1|1].tag = (T[k<<1|1].tag + T[k].tag) % mod;
T[k].tag = 0;
}
void Modify(int k, const int &ql, const int &qr, const int &aim){
int l = T[k].l, r = T[k].r;
if(T[k].tag) Push_down(k);
if(r < ql || l > qr) return ;
if(ql <= l && r <= qr){ T[k].tag = (T[k].tag + aim) % mod; Push_down(k); return ; }
int mid = l+r >> 1;
Modify(k<<1, ql, qr, aim), Modify(k<<1|1, ql, qr, aim);
T[k].s1 = (T[k<<1].s1 + T[k<<1|1].s1) % mod, T[k].s2 = (T[k<<1].s2 + T[k<<1|1].s2) % mod;
}
int Query(int k, const int &ql, const int &qr, const int &opt){
int l = T[k].l, r = T[k].r;
if(T[k].tag) Push_down(k);
if(ql <= l && r <= qr) return opt?T[k].s1:T[k].s2;
int mid = l+r >> 1, s = 0;
if(ql <= mid) s = (s + Query(k<<1, ql, qr, opt)) % mod;
if(qr > mid) s = (s + Query(k<<1|1, ql, qr, opt)) % mod;
return s;
}
} seg_t;
int main(){
N = read(), Q_ = read(), read();
fac[0] = 1; for(reg int i = 1; i <= N; i ++) fac[i] = 1ll*fac[i-1]*i % mod;
for(reg int i = 0; i <= N; i ++) p1[i] = Ksm(fac[i], mod-2);
for(reg int i = 2; i <= N; i ++) p2[i] = Ksm(1ll*i*i%mod*fac[i-2]%mod, mod-2);
ntt_len = 1; int bit_cnt = 0;
while(ntt_len <= (N << 1)) ntt_len <<= 1, bit_cnt ++;
for(reg int i = 0; i < ntt_len; i ++) rev[i] = (rev[i>>1]>>1) | ((i&1)<<bit_cnt-1);
Ntt(p1, 1), Ntt(p2, 1);
for(reg int i = 0; i < ntt_len; i ++) p1[i] = 1ll*p1[i]*p2[i] % mod;
Ntt(p1, -1); int INV = Ksm(ntt_len, mod-2);
for(reg int i = 0; i < ntt_len; i ++) p1[i] = 1ll*INV*p1[i] % mod;
seg_t.Build(1, 1, N);
for(reg int i = 1; i <= N; i ++) seg_t.Modify(1, i, i, read());
while(Q_ --){
int opt = read(), l = read(), r = read();
if(opt == 1) seg_t.Modify(1, l, r, read());
else{
int n = r-l+1, f = seg_t.Query(1, l, r, 0), g = seg_t.Query(1, l, r, 1);
g = (1ll*g*g%mod - f + mod) % mod;
int Ans = 1ll*p1[n]*fac[n-1]%mod*f%mod;
Ans -= 1ll*p1[n]*fac[n-2]%mod*g%mod; Ans += mod, Ans %= mod;
printf("%d\n", Ans);
}
}
return 0;
}