BZOJ2141 排隊——樹狀數組套權值線段樹(帶修改的主席樹)
阿新 • • 發佈:2018-08-26
upd bzoj nbsp using map space 呵呵 位置 namespace
3
130 150 140
2
2 3
1 3
0
3
【樣例說明】
未進行任何操作時,(2,3)滿足條件;
操作1結束後,序列為130 140 150,不存在滿足i<j且hi>hj的(i,j)對;
操作2結束後,序列為150 140 130,(1,2),(1,3),(2,3)共3對滿足條件的(i,j) 題目大意是求每次交換兩個數後的逆序對數。最開始沒修改時直接用樹狀數組求逆序對數就好了。因為逆序對數就是考慮每個數後面比它小的數有多少個,所以每次交換兩個數i,j對這兩個數與j後面的數產生的逆序對數沒有影響,只對i,j中間的數與兩個數產生的逆序對數有影響。考慮i,交換之後,i與i,j之間比i小的數產生的逆序對數消失了,但多了i,j之間比i大的數與i的逆序對數,j也是同樣道理。所以只要每次求i,j之間比i或j小的和大的數有多少,用主席樹維護就好了,但因為每次交換時會修改,因此要用樹狀數組套權值線段樹,也就是帶修改的主席樹。最後要註意i,j兩個數產生的逆序對數的增減。
題目描述
排排坐,吃果果,生果甜嗦嗦,大家笑呵呵。你一個,我一個,大的分給你,小的留給我,吃完果果唱支歌,大家 樂和和。紅星幼兒園的小朋友們排起了長長地隊伍,準備吃果果。不過因為小朋友們的身高有所區別,排成的隊伍 高低錯亂,極不美觀。設第i個小朋友的身高為hi,我們定義一個序列的雜亂程度為:滿足ihj的(i,j)數量。幼兒 園阿姨每次會選出兩個小朋友,交換他們的位置,請你幫忙計算出每次交換後,序列的雜亂程度。為方便幼兒園阿 姨統計,在未進行任何交換操作時,你也應該輸出該序列的雜亂程度。輸入
第一行為一個正整數n,表示小朋友的數量; 第二行包含n個由空格分隔的正整數h1,h2,…,hn,依次表示初始隊列中小朋友的身高; 第三行為一個正整數m,表示交換操作的次數; 以下m行每行包含兩個正整數ai和bi,表示交換位置ai與位置bi的小朋友。 1≤m≤2*10^3,1≤n≤2*104,1≤hi≤109,ai≠bi,1≤ai,bi≤n。輸出
輸出文件共m行,第i行一個正整數表示交換操作i結束後,序列的雜亂程度。
樣例輸入
【樣例輸入】3
130 150 140
2
2 3
1 3
樣例輸出
10
3
【樣例說明】
未進行任何操作時,(2,3)滿足條件;
操作1結束後,序列為130 140 150,不存在滿足i<j且hi>hj的(i,j)對;
操作2結束後,序列為150 140 130,(1,2),(1,3),(2,3)共3對滿足條件的(i,j) 題目大意是求每次交換兩個數後的逆序對數。最開始沒修改時直接用樹狀數組求逆序對數就好了。因為逆序對數就是考慮每個數後面比它小的數有多少個,所以每次交換兩個數i,j對這兩個數與j後面的數產生的逆序對數沒有影響,只對i,j中間的數與兩個數產生的逆序對數有影響。考慮i,交換之後,i與i,j之間比i小的數產生的逆序對數消失了,但多了i,j之間比i大的數與i的逆序對數,j也是同樣道理。所以只要每次求i,j之間比i或j小的和大的數有多少,用主席樹維護就好了,但因為每次交換時會修改,因此要用樹狀數組套權值線段樹,也就是帶修改的主席樹。最後要註意i,j兩個數產生的逆序對數的增減。
#include<set> #include<map> #include<cmath> #include<stack> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; int n,m; int x,y; int tot; int cnt; int ans; int s[20010]; int t[20010]; int a[20010]; int b[20010]; int v[20010]; int root[20010]; int ls[10000010]; int rs[10000010]; int sum[10000010]; void add(int x) { for(int i=x;i<=tot;i+=i&-i) { v[i]++; } } int ask(int x) { int res=0; for(int i=x;i;i-=i&-i) { res+=v[i]; } return res; } void change(int &rt,int l,int r,int k,int v) { if(!rt) { rt=++cnt; } if(l==r) { sum[rt]+=v; return ; } sum[rt]+=v; int mid=(l+r)>>1; if(k<=mid) { change(ls[rt],l,mid,k,v); } else { change(rs[rt],mid+1,r,k,v); } } int query_min(int l,int r,int k) { int res=0; if(l==r) { return res; } int mid=(l+r)>>1; if(k<=mid) { for(int i=1;i<=s[0];i++) { s[i]=ls[s[i]]; } for(int i=1;i<=t[0];i++) { t[i]=ls[t[i]]; } return query_min(l,mid,k); } else { for(int i=1;i<=s[0];i++) { res+=sum[ls[s[i]]]; s[i]=rs[s[i]]; } for(int i=1;i<=t[0];i++) { res-=sum[ls[t[i]]]; t[i]=rs[t[i]]; } return res+query_min(mid+1,r,k); } } int query_max(int l,int r,int k) { int res=0; if(l==r) { return res; } int mid=(l+r)>>1; if(k<=mid) { for(int i=1;i<=s[0];i++) { res+=sum[rs[s[i]]]; s[i]=ls[s[i]]; } for(int i=1;i<=t[0];i++) { res-=sum[rs[t[i]]]; t[i]=ls[t[i]]; } return res+query_max(l,mid,k); } else { for(int i=1;i<=s[0];i++) { s[i]=rs[s[i]]; } for(int i=1;i<=t[0];i++) { t[i]=rs[t[i]]; } return query_max(mid+1,r,k); } } void updata(int x,int k,int v) { for(int i=x;i<=n;i+=i&-i) { change(root[i],1,tot,k,v); } } int find_max(int l,int r,int k) { s[0]=t[0]=0; for(int i=r;i;i-=i&-i) { s[++s[0]]=root[i]; } for(int i=l;i;i-=i&-i) { t[++t[0]]=root[i]; } return query_max(1,tot,k); } int find_min(int l,int r,int k) { s[0]=t[0]=0; for(int i=r;i;i-=i&-i) { s[++s[0]]=root[i]; } for(int i=l;i;i-=i&-i) { t[++t[0]]=root[i]; } return query_min(1,tot,k); } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); b[i]=a[i]; } sort(b+1,b+1+n); tot=unique(b+1,b+1+n)-b-1; for(int i=1;i<=n;i++) { a[i]=lower_bound(b+1,b+1+tot,a[i])-b; ans+=ask(tot)-ask(a[i]); add(a[i]); updata(i,a[i],1); } printf("%d\n",ans); scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); ans+=find_min(x,y-1,a[y]); ans-=find_max(x,y-1,a[y]); ans-=find_min(x,y-1,a[x]); ans+=find_max(x,y-1,a[x]); updata(x,a[x],-1); updata(y,a[y],-1); swap(a[x],a[y]); updata(x,a[x],1); updata(y,a[y],1); if(a[x]>a[y]) { ans++; } else if(a[x]<a[y]) { ans--; } printf("%d\n",ans); } }
BZOJ2141 排隊——樹狀數組套權值線段樹(帶修改的主席樹)