1. 程式人生 > >CDQ分治的巢狀

CDQ分治的巢狀

CDQ的巢狀

上一篇部落格介紹了一下CDQ的入門思想。這裡再介紹一下它的進階,CDQ套CDQ。其實如果對入門思想掌握的透徹,巢狀也是很容易掌握的,思想是一樣的。

什麼是巢狀

簡單地說,有的問題,如果用一重CDQ來分治一個維度後,在合併時,還無法僅藉助一層資料結構(如樹狀陣列)來計算左區間對右區間元素的影響。那這時,我們可以選擇再用一重CDQ來分治下一維度,達到再降維的效果。

以一道四維偏序的變形問題為例

HDU上的一道題,stars

題意

三維空間下,有兩種操作,1.加入一個點;2.查詢當前指定長方體空間內含多少個點。

思路

這題可以類比二維下矩形求和拆成四個矩形字首求和。長方體求和可以拆成八個長方體字首求和,根據容斥做一下加減。
由於加入操作和查詢操作可能交替進行,就必須考慮操作時間的影響。解決了上一篇部落格的若干問題後,不難想到,這一題,我們必須考慮四個維度:時間和三個座標x,y,z。因此,按時間排序(即讀入順序),對x分治,在根據x進行合併時,我們發現無法簡單統計貢獻,因為還剩下兩維(y,z),也就是說我們需要二維樹狀陣列才能統計,但這樣空間開銷難以接受。因此,這一重CDQ我們不統計貢獻,只做按x排序這件事,但這樣時間順序會亂掉,所以還要標記每個元素的操作時間原本屬於左區間還是右區間。拷貝一份處理完的新序列

,這樣一來,我們再對y進行第二重CDQ分治,在根據y來合併時,此時x的影響已經處理掉了,因此需要再判斷的就是時間和操作型別,只有時間屬於左區間的改操作元素才可能對時間屬於右區間的查操作元素有貢獻。這時,剩下的一維z可以離散化後用樹狀陣列維護。

程式碼

#include<bits/stdc++.h>
#define dd(x) cout<<#x<<" = "<<x<<" "
#define de(x) cout<<#x<<" = "<<x<<"\n"
#define sz(x) int(x.size())
#define All(x) x.begin(),x.end()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> P;
typedef priority_queue<int> BQ;
typedef priority_queue<int,vector<int>,greater<int> > SQ;
const int maxn=5e4+10,mod=1e9+7,INF=0x3f3f3f3f;
int fwk[maxn<<1];
void upd(int p,int c)
{
    for (int i=p;i<(maxn<<1);i+=i&-i)
        fwk[i]+=c;
}
int qry(int p)
{
    int res=0;
    for (int i=p;i;i-=i&-i)
        res+=fwk[i];
    return res;
}
struct node
{
    int ti,x,y,z,ty,id;
};
node p[maxn<<3],tmp[maxn<<3],tmp2[maxn<<3];
int ans[maxn],id,v[maxn<<1],tot,cnt;
inline void read(int x,int y,int z,int id=0,int ty=0)
{
    p[++cnt].x=x,p[cnt].y=y,p[cnt].z=z,p[cnt].id=id,p[cnt].ty=ty;
}
inline int hs(int x)
{
    return lower_bound(v+1,v+1+tot,x)-v;
}
void cdq2(int l,int r)
{
    if (l>=r)
        return;
    int m=(l+r)>>1;
    cdq2(l,m);
    cdq2(m+1,r);
    int i=l,j=m+1,k=0;
    while (i<=m&&j<=r)
    {
        if (tmp2[i].y==tmp2[j].y? tmp2[i].ty==0 : tmp2[i].y<tmp2[j].y)
        {
            if (tmp2[i].ti==0&&tmp2[i].ty==0)
                upd(hs(tmp2[i].z),1);
            tmp[k++]=tmp2[i++];
        }
        else
        {
            if (tmp2[j].ti&&tmp2[j].ty)
                ans[tmp2[j].id]+=tmp2[j].ty*qry(hs(tmp2[j].z));
            tmp[k++]=tmp2[j++];
        }
    }
    while (j<=r)
    {
        if (tmp2[j].ti&&tmp2[j].ty)
            ans[tmp2[j].id]+=tmp2[j].ty*qry(hs(tmp2[j].z));
        tmp[k++]=tmp2[j++];
    }
    for (int t=l;t<i;++t)
        if (tmp2[t].ti==0&&tmp2[t].ty==0)
            upd(hs(tmp2[t].z),-1);
    while (i<=m)
        tmp[k++]=tmp2[i++];
    for (int i=0;i<k;++i)
        tmp2[l+i]=tmp[i];
}
void cdq1(int l,int r)
{
    if (l>=r)
        return;
    int m=(l+r)>>1;
    cdq1(l,m);
    cdq1(m+1,r);
    int i=l,j=m+1,k=0;
    while (i<=m&&j<=r)
    {
        if (p[i].x==p[j].x? (p[i].ty==0) : (p[i].x<p[j].x))
            p[i].ti=0, tmp[k++]=p[i++];
        else
            p[j].ti=1, tmp[k++]=p[j++];
    }
    while (i<=m)
        p[i].ti=0, tmp[k++]=p[i++];
    while (j<=r)
        p[j].ti=1, tmp[k++]=p[j++];
    for (i=0;i<k;++i)
        p[l+i]=tmp[i];      
    for (i=0;i<k;++i)
        tmp2[i]=tmp[i];
    cdq2(0,k-1);
}
int main()
{
    int T;
    cin>>T;
    while (T--)
    {
        memset(ans,0,sizeof(ans));
        tot=0,cnt=0,id=0;
        int n;
        scanf("%d",&n);
        for (int i=1;i<=n;++i)
        {
            int op,x1,x2,y1,y2,z1,z2;
            scanf("%d",&op);
            if (op==1)
            {
                scanf("%d%d%d",&x1,&y1,&z1);
                read(x1,y1,z1);
            }
            else
            {
                scanf("%d%d%d%d%d%d",&x1,&y1,&z1,&x2,&y2,&z2);
                x1--,y1--,z1--;
                read(x2,y2,z2,++id,1);
                read(x1,y1,z2,id,1);
                read(x1,y2,z2,id,-1);
                read(x2,y1,z2,id,-1);
                read(x2,y2,z1,id,-1);
                read(x1,y1,z1,id,-1);
                read(x1,y2,z1,id,1);
                read(x2,y1,z1,id,1);
                v[++tot]=z2;
            }
            v[++tot]=z1;
        }
        sort(v+1,v+1+tot);
        cdq1(1,cnt);
        for (int i=1;i<=id;++i)
            printf("%d\n",ans[i]);
    }
    return 0;
}

總結

簡單總結一下,可以cdq分治的這類題,就是用cdq來逐步降維,把高維問題簡化為我們熟悉的低維問題。當然資料結構也可以起到降維的效果,但缺點一般是空間開銷較大。因此在不強制線上的情況下,CDQ分治不妨作為一個降維工具