我是看这个学会的:mlystdcall
咕咕咕了好久的一个东西。
使用该算法的两个条件:
- 题目允许使用离线算法;
- 答案无后效性,即修改区间中位置上5的值,不会对区间[1,4]的查询产生影响。
定义
s
o
l
v
e
(
l
,
r
)
solve(l,r)
solve(l,r)表示对于
∀
k
∈
[
l
,
r
]
{\forall}k\in[l,r]
∀k∈[l,r]若第
k
k
k项操作是查询,则计算第
[
l
,
k
−
1
]
[l,k-1]
[l,k−1]中的所有修改操作对它造成的影响。
计算方法:
- m i d = ( l + r ) > > 1 mid=(l+r)>>1 mid=(l+r)>>1,递归计算 s o l v e ( l , m i d ) solve(l,mid) solve(l,mid)
- 递归计算 s o l v e ( m i d + 1 , r ) solve(mid+1,r) solve(mid+1,r)
- 计算 [ l , m i d ] [l,mid] [l,mid]中所有的修改对 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]中的所有查询造成的影响。
为什么这样做是对的?
对于
[
l
,
m
i
d
]
[l,mid]
[l,mid]中的所有修改对查询造成的影响已经在递归处理
s
o
l
v
e
(
l
,
m
i
d
)
solve(l,mid)
solve(l,mid)中计算完毕了,
[
m
i
d
+
1
,
r
]
[mid+1,r]
[mid+1,r]中的也已经递归计算完毕了,那么只剩下
[
l
,
m
i
d
]
[l,mid]
[l,mid]中的修改对于
[
m
i
d
+
1
,
r
]
[mid+1,r]
[mid+1,r]中的查询造成的影响了,故最后处理这一部分的影响。
从一道例题出发吧:
支持单点修改,区间查询。
很裸的树状数组。
这里拿cdq来写,定义结构体:
struct Node{
int type;//操作类型;
int idx;//操作的位置;
int val;//修改的值或者表示第几个查询;
bool operator <(const Node& x)const{//优先按照操作位置,其次是修改优于查询;
if(idx!=x.idx) return idx<x.idx;
return type<x.type;
}
};
修改操作type为1,然后将查询
[
l
,
r
]
[l,r]
[l,r]操作拆解成两个,
s
u
m
[
r
]
−
s
u
m
[
l
−
1
]
sum[r]-sum[l-1]
sum[r]−sum[l−1],对于
l
−
1
l-1
l−1的查询,type=2,
r
r
r的查询,type=3。
因为我们在输入的时候就是按照操作的时间顺序进行输入的,所以在分治过程中拆成的两半区间,左区间的所有操作一定是发生在右区间之前的,所以我们只记录左边的修改造成的影响,然后将其加到对应的右边的操作即可。
注意到我们查询拆成了两个前缀和的形式,那么判断修改对查询是否造成了影响,只需要
- 在时间线上满足时间顺序
- 修改位置要小于查询位置,(修改5位置上的数字,只会对查询
[
5
,
+
o
o
]
[5,+oo]
[5,+oo]才会产生影响)‘’
P2068 统计和
这道题数列没有初始值,如果有初始值的话,可以将初始值看成修改操作即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
const int maxm=2e4+7;
struct Node{
int type;
int idx;
int val;
bool operator <(const Node& x)const{
if(idx!=x.idx) return idx<x.idx;
return type<x.type;
}
}query[maxm],temp[maxm];
ll ans[maxm];
void CDQ(int l,int r){
if(l==r) return ;
int mid=(l+r)>>1;
CDQ(l,mid);
CDQ(mid+1,r);
int p=l,q=mid+1;
ll sum=0;
int o=0;
while(p<=mid&&q<=r){
if(query[p]<query[q]){//只记录左边的修改造成的影响;
if(query[p].type==1) sum+=query[p].val;
temp[o++]=query[p++];
}
else{//说明在此刻,右边的操作的位置要大于左边操作的位置,
//那么由于我们记录的是前缀和,故这一刻,左边的修改就需要加到右边的这次查询中了。
if(query[q].type==2) ans[query[q].val]-=sum;
else if(query[q].type==3) ans[query[q].val]+=sum;
temp[o++]=query[q++];
}
}
while(p<=mid) temp[o++]=query[p++];
while(q<=r){
if(query[q].type==2) ans[query[q].val]-=sum;
else if(query[q].type==3) ans[query[q].val]+=sum;
temp[o++]=query[q++];
}
for(int i=0;i<o;++i) query[l+i]=temp[i];
}
char s[9];
int main(){
int n,w;
scanf("%d%d",&n,&w);
int l,r,id,x;
int idx=0,time=0;
for(int i=1;i<=w;++i){
scanf("%s",s);
if(s[0]=='x'){
scanf("%d%d",&l,&x);
++idx;
query[idx].type=1,query[idx].idx=l,query[idx].val=x;
}
else{
scanf("%d%d",&l,&r);
++idx;
++time;
query[idx].type=2,query[idx].idx=l-1,query[idx].val=time;
++idx;
query[idx].type=3,query[idx].idx=r,query[idx].val=time;
}
}
CDQ(1,idx);
for(int i=1;i<=time;++i) printf("%lld\n",ans[i]);
return 0;
}
1690

被折叠的 条评论
为什么被折叠?



