CDQ分治的一些理解

我是看這個學會的mlystdcall
咕咕咕了好久的一個東西。
使用該算法的兩個條件:

  1. 題目允許使用離線算法;
  2. 答案無後效性,即修改區間中位置上5的值,不會對區間[1,4]的查詢產生影響。

定義solve(l,r)solve(l,r)表示對於k[l,r]{\forall}k\in[l,r]若第kk項操作是查詢,則計算第[l,k1][l,k-1]中的所有修改操作對它造成的影響。
計算方法:

  1. mid=(l+r)>>1mid=(l+r)>>1,遞歸計算solve(l,mid)solve(l,mid)
  2. 遞歸計算solve(mid+1,r)solve(mid+1,r)
  3. 計算[l,mid][l,mid]中所有的修改對[mid+1,r][mid+1,r]中的所有查詢造成的影響。

爲什麼這樣做是對的?
對於[l,mid][l,mid]中的所有修改對查詢造成的影響已經在遞歸處理solve(l,mid)solve(l,mid)中計算完畢了,[mid+1,r][mid+1,r]中的也已經遞歸計算完畢了,那麼只剩下[l,mid][l,mid]中的修改對於[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]操作拆解成兩個,sum[r]sum[l1]sum[r]-sum[l-1],對於l1l-1的查詢,type=2,rr的查詢,type=3。
因爲我們在輸入的時候就是按照操作的時間順序進行輸入的,所以在分治過程中拆成的兩半區間,左區間的所有操作一定是發生在右區間之前的,所以我們只記錄左邊的修改造成的影響,然後將其加到對應的右邊的操作即可。
注意到我們查詢拆成了兩個前綴和的形式,那麼判斷修改對查詢是否造成了影響,只需要

  1. 在時間線上滿足時間順序
  2. 修改位置要小於查詢位置,(修改5位置上的數字,只會對查詢[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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章