動態逆序對 CDQ

2021-07-10 02:14:17 字數 2732 閱讀 3939

cdq二層試煉:

bzoj3295

題意大致是:

給你乙個序列(乙個1~n的排列),每次刪掉乙個數,求刪掉之前的逆序對。

方案1:強行模擬,每次刪掉乙個乙個數,在剩下的數裡面用歸併求逆序對,複雜度o(mnlogn)

這個複雜度明顯是不能接受的

方案2:樹套樹,**量令人傷心。

方案3:cdq分治。

之前已經寫過一篇**cdq的了,那到底什麼是cdq呢?

我認為cdq是一種思想,一種分治的思想。在三維偏序中cdq的思想就是一維排序,二維分治,三維樹狀陣列。

在這道題裡面,我們可以把這道題看成是求(x,y,z)這個點被刪除後對總答案的影響。

我們把 當前位置i作為x,把i位置的數的大小作為y ,把刪除時間作為z(初始為n||無窮大,就是對答案無影響)

接下來考慮 刪去(x,y,z)對答案有什麼影響。  首先一點最容易想到,它只會影響到zi>z的點(之前的點都已經被刪去,那些點不會影響到逆序對的個數).

於是,我們就可以對z這一維排序。(我是從大到小排的,這樣處理比較方便)

這樣,刪去乙個點只會對在它左邊的點的某些點構成逆序對的減少。

在zi>z的情況下接著考慮刪去(x,y,z)的影響:

如果xi>x&&yi

如果xiy(這個點在當前點前面&&這個點的值》當前點的值)構成乙個逆序對,刪去之後逆序對-1

接下來怎麼做?

z這一維已經排好序了,我們仍然進行分治。

(下面以分治x,資料結構維護y為例。              ps:我的**是分治y,資料結構維護x,大家可以推一下,熟悉一下)

考慮[l,mid],[mid+1,r]的2分區間,  分別將按x進行兩次排序.

然後考慮左區間對右區間的影響:

(這裡是求刪去當前點對答案的影響)

①按x遞減排序,  列舉右區間j,在左區間一直更新 i.x>j.x的i,將i.y在樹狀陣列中的值+1(逆序對數+1),然後查詢[1,j.y)這個區間的和,把它累加到當前點的影響中。

按x遞增排序,  列舉右區間的j,在左區間一直更新 i.x

然後就做完了.

分治的時候先呼叫左區間,再呼叫右區間,然後計算左區間對右區間的影響。(其實先後順序可以交換,習慣而已)

然後建議自己推一下分治y,維護x的情況(我的**也是這種)

然後寫這一篇文章的目的是加深對cdq的理解,but我光榮的調了好久的bug,從wa成t,最後改了一下分治的sort,優化了常數才過的!

附上ac**:

#include#include#include#include#include#include#include#define ll long long

using namespace std;

const ll maxn=100000+20;

ll n,m;

struct node

q[maxn],r1[maxn],r2[maxn];

ll shan[maxn];

bool cmpy(node a,node b)

bool cmpx(node a,node b)

long long fl[maxn],fr[maxn];

/*恆保證 左區間z >右區間 刪除右區間某個值, 對答案的貢獻是

對於乙個點(x,y,z)刪掉它減少的逆序對是左區間 zi>z :

xi>x yiy 記為fl

*/ long long f[maxn];

void updata(ll i)

}void clear(ll i)

}ll query(ll i)

return ans;

}void work(ll l,ll r)

for(ll i=1;i<=rt1;i++)clear(r1[i].x);

//計算所有xi>x yi=1;j--)

for(ll i=l;i<=mid;i++)clear(q[i].y);

sort(q+l,q+r+1,cmpz);

*/}void cal(ll l,ll r)

long long sum=0;

long long t[maxn];

long long a[maxn],b[maxn];

long long cao[maxn];

void guibing(ll l,ll r)

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

ll t1,t2;

t1=t2=0;

guibing(l,mid);

for(ll i=l;i<=mid;i++)a[i]=cao[i];

guibing(mid+1,r);

for(ll i=mid+1;i<=r;i++)b[i]=cao[i];

ll i=l,j=mid+1;

ll tt=l;

while(i<=mid&&j<=r)

else cao[tt++]=a[i++];

} while(i<=mid)cao[tt++]=a[i++];

while(j<=r)cao[tt++]=b[j++];

}int main()

sort(q+1,q+n+1,cmpy);

for(ll i=1;i<=m;i++)

sort(q+1,q+n+1,cmpz);

cal(1,n);

// guibing(1,n);

for(ll i=1;i<=m;i++)

return 0;

}

動態逆序對 CDQ分治

對於序列a,它的逆序對數定義為滿足iaj的數對 i,j 的個數。給1到n的乙個排列,按照某種順序依次刪除m個元素,你的任務是在每次刪除乙個元素之前統計整個序列的逆序對數。輸入格式 輸入第一行包含兩個整數n和m,即初始元素的個數和刪除的元素個數。以下n行每行包含乙個1到n之間的正整數,即初始排列。以下...

Luogu1393 動態逆序對(CDQ分治)

對於給定的一段正整數序列,我們定義它的逆序對的個數為序列中ai aj且i j的有序對 i,j 的個數。你需要計算出乙個序列的逆序對組數及其刪去其中的某個數的逆序對組數。輸入格式 第一行,兩個數n,m,表示序列中有n個數,要刪去m個數 第二行n個數,表示給定的序列。第三行m個數,第i個數di表示要刪去...

bzoj3295 動態逆序對(CDQ分治)

time limit 10 sec memory limit 128 mb submit 7178 solved 2548 submit status discuss 對於序列a,它的逆序對數定義為滿足iaj的數對 i,j 的個數。給1到n的乙個排列,按照某種順序依次刪 除m個元素,你的任務是在每次...