[摸鱼]cdq分治 && 学习笔记
待我玩会游戏整理下思绪(分明是想摸鱼
cdq分治是一种用于降维和处理对不同子区间有贡献的离线分治算法
对于常见的操作查询题目而言,时间总是有序的,而cdq分治则是耗费\(O(logq)\)的代价使动态操作化为静态查询问题(the world!
考虑无修改的求逆序对问题
每个元素可定义为\((pos_i,val_i)\),求对每个\((pos_i,val_i)\)有多少个\((pos_j,val_j)\),满足\(pos_j<pos_i,val_j>val_i\)
cdq分治的过程就是令其中一维有序(pos),计算出贡献消除该维度的影响,后面对已遍历的元素只需得知\(val\)的关系即可
因此对于归并过程的merge中假设\([l,mid]\)和\([mid+1,r]\)的子区间已经统计完,保证了两个子区间分别有序,那只需再求左子区间对右子区间的贡献即可
比如左子区间中的下标\(p\)和右子区间中的下标\(q\)满足\(val_p>val_q\),那么可以得出\(val_{[p...mid]}>val_q\),左区间对于右区间中的\(q\)的贡献为\(mid-p+1\),统计完后继续维护大区间的有序并pushup即可
而对于有修改(既存在时间变量)的操作,我们需要维护左子区间的修改对右区间查询的影响(因为对于分治,左区间存在是右区间存在的前提),对于查询则需要标记时间的维度\(ansid\)
注意如果\(p\)和\(q\)优先越界的处理上的不同
以及区间查询时一分为二的做法
练手题 Luogu - P3374
题意:m次操作,单点更新,区间查询
我们把原数组的初始值当作插入修改来处理,时间复杂度\(O((m+n)log(m+n))\)
#include<bits/stdc++.h>
#define rep(i,j,k) for(register int i=j;i<=k;i++)
#define rrep(i,j,k) for(register int i=j;i>=k;i--)
#define erep(i,u) for(register int i=head[u];~i;i=nxt[i])
#define print(a) printf("%lld",(ll)(a))
#define printbk(a) printf("%lld ",(ll)(a))
#define println(a) printf("%lld\n",(ll)(a))
using namespace std;
const int MAXN = 1.5e6+11;
typedef long long ll;
const ll MOD = 1e9+7;
const ll INF = 1ll<<60;
unsigned int SEED = 19260817;
ll read(){
ll x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct QUERY{
int pos,val,type;
bool operator < (const QUERY &rhs) const{
if(pos!=rhs.pos) return pos<rhs.pos;
return type<rhs.type;
}
}Q[MAXN],tmp[MAXN];
ll ans[MAXN];
void solve(int l,int r){
if(l==r)return;
int mid=l+r>>1;
solve(l,mid);
solve(mid+1,r);
int p=l,q=mid+1,cnt=0;
ll sum=0;
while(p<=mid&&q<=r){
if(Q[p]<Q[q]){
if(Q[p].type==1) sum+=Q[p].val;
tmp[++cnt]=Q[p++];
}else{
if(Q[q].type==2) ans[Q[q].val]-=sum;
if(Q[q].type==3) ans[Q[q].val]+=sum;
tmp[++cnt]=Q[q++];
}
}
while(p<=mid) tmp[++cnt]=Q[p++];
while(q<=r){
if(Q[q].type==2) ans[Q[q].val]-=sum;
if(Q[q].type==3) ans[Q[q].val]+=sum;
tmp[++cnt]=Q[q++];
}
rep(i,1,cnt) Q[i+l-1]=tmp[i];
}
int main(){
int m,n;
while(cin>>n>>m){
int cnt=0,ansid=0;
rep(i,1,n){
Q[++cnt].pos=i;
Q[cnt].val=read();
Q[cnt].type=1;
}
rep(i,1,m){
int op=read();
if(op==1){
Q[++cnt].pos=read();
Q[cnt].val=read();
Q[cnt].type=1;
}else{
int l=read();
int r=read();
Q[++cnt].pos=l-1;
Q[cnt].val=++ansid;
Q[cnt].type=2;
Q[++cnt].pos=r;
Q[cnt].val=ansid;
Q[cnt].type=3;
}
}
solve(1,cnt);
rep(i,1,ansid) println(ans[i]);
}
return 0;
}