就是三倍經驗
維護乙個序列,每次修改後求出當前序列逆序對個數。
題目讓我們求出
\[\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之間的正整數,即...