玄學演算法CDQ分治

2021-07-30 22:32:27 字數 1706 閱讀 2866

啊剛學這個啊

剛過了一道題就屁顛屁顛來寫部落格很虛啊

bzoj4553

就是這道題

說一下我對cdq分治的理解

我感覺。。這個就類似於。。把暴力,轉化為。。容易優化的暴力。。然後優化??

並且一般只用於處理問題具有單調性的題,即f[i]對任意f[j](j=,不產生影響。

例如最長上公升子串行的問題

我們本來需要列舉這個j,但是通過cdq分治就不需要去全部列舉了

我們處理1~n的時候,如果1~n/2已經全部處理完畢,那麼就可以用1~n/2的結果去更新n/2+1~n的結果

注意 這裡是更新,而不是算出

因為等會兒我們會列舉n/2+1~n/2+n/4去更新n/2+n/4+1~n

這樣,f[i]就會被所有比他小的點更新

這裡仍然舉最長上公升子串行的例子,暴力dp顯然是n^2,然而上面說的這個想法,如果不加任何處理。。也是n^2,甚至常數更大

但是我們可以對前面的值進行一些處理,比如按照a[i]排序。

同時對後面的值處理,那麼我們就可以線性推,把一次更新的時間優化到2(r-l),整個時間效率也就優化到了n*log n

那麼再來看看看看一開始說的那道題,其實我們只要

a[i]正確性顯然

那麼考慮cdq分治

這次有二維的條件,顯然乙個排序後仍然不能確保前面的值全部可以取,那麼怎麼辦呢

排序後顯然只有一維的條件了,我們再套乙個cdq我們可以用樹狀陣列,記錄權值的字首和就可以了

自己也不太懂啊就這樣吧

code:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define inf 1e9

#define ll long long

#define for(i,j,k) for(ll i=j;i<=k;i++)

#define dow(i,j,k) for(ll i=k;i>=j;i--)

using

namespace

std;

int n,m,a[100001],f[100001],ff[100001],mi[100001],mx[100001],num[100001];

inline

bool cmp1(int x,int y)

void add(int x,int v)

int get(int x)

void solve (int l,int r)

int mid=(l+r)>>1;

solve(l,mid);

for(i,l,r) num[i]=i;

sort(num+l,num+mid+1,cmp2);sort(num+mid+1,num+r+1,cmp1);

int poi=l;

for(i,mid+1,r)

for(i,l,poi) add(mx[num[i]],0);

solve(mid+1,r);

}int main()

solve(1,n);

int ans=0;

for(i,1,n) ans=max(ans,f[i]);

printf("%d\n",ans);

}

CDQ分治概述

log l og 的時間把它變成離線問題。正好有些題目的離線問題是比較簡單的。具體是什麼意思呢?我們對於每一層分治,只考慮前一半對於後一半的影響,然後在每個詢問當中記錄下來影響。最後把所有影響合併就可以得到每乙個詢問的答案。舉個例子 區間修改區間查詢。首先,在時間軸上離線分治。每一層分治後把詢問和查...

CDQ分治總結

cdq這個東西嘛,說容易其實也很容易,說難其實也有些難,但只要細細品味,定能發現其中的真理的!那真理,也會像蝴蝶一般,破蛹而出,化身為一道亮麗的風景線。題記。咳咳,閒話就講到這裡了,切入正題。首先我們來了解一下cdq分治這個東東。cdq分治,他的常數小,但必須離線操作the most importa...

cdq分治小結

一般的分治,眾所周知的,是通過將大的問題拆小,然後對小問題的答案進行合併得到大問題的答案,但是cdq分治不是。我們知道,分治時,將乙個區間從中間斬開,分兩半處理,cdq分治在處理完之後,不是合併答案,而是計算左區間對右區間的貢獻,這樣子可以將維度降低,問題就更好做了。現在有 n nn 個二元組,每個...