1. 找多次区间排序后某个位置的值
先将问题简单化,变为 01 01 01 序列, 01 01 01 序列的处理用线段树区间修改+区间/单点查询。
转化方法:二分 + + + 线段树
二分值,大于等于它的为 1 1 1,小于它的为 0 0 0
2. 等差数列的差分
进行两次差分
因为第一次差分它们会有一段的值都相等,并且修改时时间复杂度很大,所以我们再进行一次差分,只有 l , l + 1 , r + 1 , r + 2 l,l+1,r+1,r+2 l,l+1,r+1,r+2 这两个位置的值改变了,可以 O ( 1 ) O(1) O(1) 修改。
最后统计答案时用两个数组求前缀和,一个用于求出当前数与上一个数的差值,一个用于求出当前数。
3. 可以从前后门上车
我们建立一个 n + 1 n+1 n+1 号点,使它与 1 1 1 与 n n n 相连
4. k*x+b 类计算值
类似这样的式子
(
k
1
∗
x
+
b
1
)
∗
k
2
+
b
2
=
k
1
∗
k
2
∗
x
+
k
2
∗
b
1
+
b
2
(k_1 * x + b_1 )*k_2+b_2=k_1*k_2*x+k_2*b_1+b_2
(k1∗x+b1)∗k2+b2=k1∗k2∗x+k2∗b1+b2
可以使用跳表来解决
p
[
x
]
[
i
]
p[x][i]
p[x][i] 表示从
x
x
x 向后跳
2
i
2^i
2i 步走到的点(不包含
x
x
x)
k
[
x
]
[
i
]
k[x][i]
k[x][i] 表示从
x
x
x 开始(包含
x
x
x)一共走
2
i
2^i
2i 步,这个长度的区间的
k
k
k 的值
b
[
x
]
[
i
]
b[x][i]
b[x][i] 表示从
x
x
x 开始(包含
x
x
x)一共走
2
i
2^i
2i 步,这个长度的区间的
b
b
b 的值
转移方程可写:
p
[
x
]
[
i
]
=
p
[
p
[
x
]
[
i
−
1
]
]
[
i
−
1
]
;
k
[
x
]
[
i
]
=
k
[
x
]
[
i
−
1
]
∗
k
[
p
[
x
]
[
i
−
1
]
]
[
i
−
1
]
;
b
[
x
]
[
i
]
=
k
[
p
[
x
]
[
i
−
1
]
]
[
i
−
1
]
∗
b
[
x
]
[
i
−
1
]
+
b
[
p
[
x
]
[
i
−
1
]
]
[
i
−
1
]
;
p[x][i]=p[p[x][i-1]][i-1];\\ k[x][i]=k[x][i-1]*k[p[x][i-1]][i-1];\\ b[x][i]=k[p[x][i-1]][i-1]*b[x][i-1]+b[p[x][i-1]][i-1];
p[x][i]=p[p[x][i−1]][i−1];k[x][i]=k[x][i−1]∗k[p[x][i−1]][i−1];b[x][i]=k[p[x][i−1]][i−1]∗b[x][i−1]+b[p[x][i−1]][i−1];
例题:[HDU] Calculate
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+10;
const ll mod=1e9+7;
ll b[N][31],k[N][31];
int q,n,p[N][31];
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
scanf("%lld",&k[i][0]);
for(int i=1;i<=n;i++)
scanf("%lld",&b[i][0]);
for(int i=1;i<=n;i++)
scanf("%d",&p[i][0]);
for(int i=1;i<=30;i++)
for(int x=1;x<=n;x++){
p[x][i]=p[p[x][i-1]][i-1];
k[x][i]=k[x][i-1]*k[p[x][i-1]][i-1]%mod;
b[x][i]=(k[p[x][i-1]][i-1]*b[x][i-1]%mod+b[p[x][i-1]][i-1])%mod;
}
int x,l; ll y;
while(q--){
scanf("%d%d%lld",&x,&l,&y);
x=p[x][0];
for(int i=0;i<=30;i++){
if((l>>i)&1){
y=(y*k[x][i]%mod+b[x][i])%mod;
x=p[x][i];
}
}
printf("%lld\n",y);
}
}
return 0;
}