動態逆序對專練

2022-05-30 17:42:10 字數 3877 閱讀 8194

就是三倍經驗

維護乙個序列,每次修改後求出當前序列逆序對個數。

題目讓我們求出

\[\sum_^n\sum_^n[a_i>a_j]

\]也就是讓我們求出滿足

\[pos_ia_j

\]的點對數量。

對於不修改的情況,這顯然是乙個三維偏序問題,用樹狀陣列或歸併處理都可以。

我這麼懶當然是用樹套樹啦~

樹狀陣列維護序列,值域線段樹維護值域。

優點是可以寫成非遞迴式查詢,常數相對較小。

缺點是即使是動態開點空間消耗仍然很大。

具體實現是每個樹狀陣列節點開一棵線段樹。修改時修改所有樹狀陣列上包括的線段樹,查詢時類似。

void update(int &ro,int l,int r,int x,int k)
牆裂安利非遞迴式線段樹查詢

inline long long query(int l,int r,int x,int type)

for(int i=1;i<=cnta;i++) qa[i]=rs[qa[i]];

for(int i=1;i<=cntb;i++) qb[i]=rs[qb[i]];

l=mid+1;

}else

for(int i=1;i<=cnta;i++) qa[i]=ls[qa[i]];

for(int i=1;i<=cntb;i++) qb[i]=ls[qb[i]];

r=mid;}}

return ans;

}

給你乙個排列,每次刪除乙個位置上的數,求每次操作後的逆序對數。

插入原序列之後得到原序列答案,每次刪除乙個數 \(x\) 查詢 \(pos_i、\(a_i>a_x\) 和 \(pos_i>x\) 、\(a_i的答案並用之前的答案減去,然後刪除它的影響。

#include#include#include#include#include#includeusing namespace std;

inline int read()

namespace star

int qa[maxn],qb[maxn];

long long ans;

inline long long query(int l,int r,int x,int type)

for(int i=1;i<=cnta;i++)qa[i]=rs[qa[i]];

for(int i=1;i<=cntb;i++)qb[i]=rs[qb[i]];

l=mid+1;

}else

for(int i=1;i<=cnta;i++)qa[i]=ls[qa[i]];

for(int i=1;i<=cntb;i++)qb[i]=ls[qb[i]];

r=mid;

}} return ans;

} int pos[maxn];

inline void work()

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

while(--m) }}

signed main()

給你乙個原序列為遞增排列的序列,每次交換兩個位置上的數,求每次操作後的逆序對數。

相對於上一題,並非刪除而是交換兩個位置上的數,實際上就是在原位置刪除兩個數然後在彼此的位置又加上這兩個數。

這裡我先減去影響然後更新再加上影響,最後單獨討論一下這兩個數之間互換對答案的貢獻。

#include#include#include#include#include#includeusing namespace std;

inline int read()

namespace star

int qa[maxn],qb[maxn];

inline long long query(int l,int r,int x,int type)

for(int i=1;i<=cnta;i++) qa[i]=rs[qa[i]];

for(int i=1;i<=cntb;i++) qb[i]=rs[qb[i]];

l=mid+1;

}else

for(int i=1;i<=cnta;i++) qa[i]=ls[qa[i]];

for(int i=1;i<=cntb;i++) qb[i]=ls[qb[i]];

r=mid;

}} return ans;

} inline void work()

while(q--)

if(x>y)swap(x,y);

ans=ans-query(1,x-1,a[x],0)-query(x+1,n,a[x],1)-query(1,y-1,a[y],0)-query(y+1,n,a[y],1);

for(int i=x;i<=n;i+=i&-i) update(rt[i],1,n,a[x],-1),update(rt[i],1,n,a[y],1);

for(int i=y;i<=n;i+=i&-i) update(rt[i],1,n,a[x],1),update(rt[i],1,n,a[y],-1);

swap(a[x],a[y]);

ans=ans+query(1,x-1,a[x],0)+query(x+1,n,a[x],1)+query(1,y-1,a[y],0)+query(y+1,n,a[y],1);

ans+=(a[x]給你乙個序列,每次交換兩個位置上的數,求每次操作後逆序對數。

給上面的**加個離散化xd

後面單獨討論兩個數的貢獻時有一點區別。

#include#include#include#include#include#includeusing namespace std;

inline int read()

namespace star

int qa[maxn],qb[maxn];

inline long long query(int l,int r,int x,int type)

for(int i=1;i<=cnta;i++) qa[i]=rs[qa[i]];

for(int i=1;i<=cntb;i++) qb[i]=rs[qb[i]];

l=mid+1;

}else

for(int i=1;i<=cnta;i++) qa[i]=ls[qa[i]];

for(int i=1;i<=cntb;i++) qb[i]=ls[qb[i]];

r=mid;

}} return ans;

} inline void work()

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

q=read();

while(q--)

if(x>y)swap(x,y);

ans=ans-query(1,x-1,a[x],0)-query(x+1,n,a[x],1)-query(1,y-1,a[y],0)-query(y+1,n,a[y],1);

for(int i=x;i<=n;i+=i&-i) update(rt[i],1,n,a[x],-1),update(rt[i],1,n,a[y],1);

for(int i=y;i<=n;i+=i&-i) update(rt[i],1,n,a[x],1),update(rt[i],1,n,a[y],-1);

swap(a[x],a[y]);

ans=ans+query(1,x-1,a[x],0)+query(x+1,n,a[x],1)+query(1,y-1,a[y],0)+query(y+1,n,a[y],1);

if(a[x]a[y]) ans-=1;

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

} }}signed main()

複製了三遍**顯得很長的雅子

對於後兩個題更簡單並且更優秀的分塊解法,我不會因為沒有普適性所以我們不學,嗯嗯。

省選專練CQOI2016動態逆序對

這真的是主席樹?為什麼我認為是線段樹動態開點。ok內容非常簡單 利用這個不知道是什麼的資料結構維護已刪除區間內比他小的數 這個反正是動態區間第k大的資料結構可過的 但是為什麼會在 update函式裡面寫 p呢?這又不符合主席樹版本更新的思想。後來思考了一下 因為主席樹做了差分。不需要版本了 incl...

動態逆序對

容易寫掛 對於新手與蒟蒻 洛谷 cdq 如果按照三維偏序那樣求,那麼會漏掉一些情況。所以要跑兩遍cdq。兩遍cdq又會有乙個問題,就是判等於的問題。第一遍cdq第三維判等於,第二遍判不等於.include define ll long long using namespace std inline ...

動態逆序對

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