线性优化策略
需要的前备知识: 乘法逆元, 容斥定理, 倍增求lca
所谓线性优化,即维护一个序列并进行操作
前缀和
定义
设原序列为 a 1 … n a_{1…n} a1…n, 则前缀和序列为
s u m i = ∑ k = 1 i a k sum_i=∑^i_{k=1}a_k sumi=k=1∑iak
代码
sum[0] = 0;
for(int i = 1; i <= n; i++)
sum[i] += a[i];
ans = sum[i] - sum[i - 1];//单点查询
ans = sum[r] - sum[l - 1]//区间查询,注意是l-1!
for(int i = j, i <= n; i++)//单点修改
sum[i] += k;
for(int i = l; i <= r; i++)//区间修改
sum[i] += (i - l + 1) * k;
for(int i = r + 1; i <= n; i++)
sum[i] += (r - l + 1) * k;
前缀和の扩展
前缀\乘\异或
除了可以用前缀和来求快速区间和外,不难想到也可以来求快速区间乘\异或。
这是有人就会问,能不能有前缀和的求法来求快速区间减\除\或操作呢?
答案显然是不行的。
那么减法、除法、或操作到底与加法、乘法、异或有什么区别吗?
因为它们不符合结合律和逆运算性质。
为什么要符合结合律呢,因为符合结合律性质的操作,其数值地位都是平等的,可以进行累积操作和处理。但是减法或除法就不行,因为有被减\除数和减\除数,其地位不等,当然无法使用前缀和算法。
而或符合结合律啊,那为什么不行呢?因为或运算没有逆运算。而没有逆运算当然就无法维护原序列了。
所以说,满足前缀和算法的操作应当符合结合律和逆运算两个性质。
诶,是不是很像群?
代码:
for(int i = 1; i <= n; i++)
// sum[i] = sum[i - 1] + a[i];
// sum[i] = sum[i - 1] * (a[i] % p) % p;
sum[i] = sum[i - 1] ^ a[i];
// ans = sum[r] - sum[l - 1];
// ans = sum[r] * quickpow(sum[l - 1], p - 2) % p; //需要用到乘法逆元
ans = sum[r] ^ sum[l - 1];
二维前缀和
一维前缀和是维护序列的和,那么二维前缀和自然是维护矩阵的和了。
给定一个矩阵 a 1 , 1 … n , n a_{1,1…n,n} a1,1…n,n,求 s u m x , y = ∑ i = 1 x ∑ j = 1 y a i , j sum_{x,y}=∑^x_{i=1}∑^y_{j=1}a_{i,j} sumx,y=∑i=1x