/* 结果肯定和 逆序对数量有关,假设当前求第k个答案: 如果1-k元素连续,则只要求出1-k的逆序对个数即可 如果不连续,那么先把这k个元素移动到一起,然后再求逆序对 移动的策略是二分找中间位置p,p左边的元素数量=p右边的元素数量 所以用线段树去维护当前已经存在的点位置 每次求答案:二分找位置p,求最小移动代价,求逆序对 */ #include<bits/stdc++.h> using namespace std; #define ll long long #define N 200005 int n; struct Node{ int pos,v; }p[N]; int cmp(Node &a, Node & b){return a.v<b.v;} #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 ll sum1[N<<2],sum2[N<<2]; void update(int pos,int l,int r,int rt){ if(l==r){sum1[rt]=1;sum2[rt]=l;return;} int m=l+r>>1; if(pos<=m)update(pos,lson); else update(pos,rson); sum1[rt]=sum1[rt<<1]+sum1[rt<<1|1]; sum2[rt]=sum2[rt<<1]+sum2[rt<<1|1]; } ll query1(int L,int R,int l,int r,int rt){ if(L>R)return 0; if(L<=l && R>=r)return sum1[rt]; int m=l+r>>1; ll res=0; if(L<=m)res+=query1(L,R,lson); if(R>m)res+=query1(L,R,rson); return res; } ll query2(int L,int R,int l,int r,int rt){ if(L>R)return 0; if(L<=l && R>=r)return sum2[rt]; int m=l+r>>1; ll res=0; if(L<=m)res+=query2(L,R,lson); if(R>m)res+=query2(L,R,rson); return res; } int query(int k,int l,int r,int rt){ if(l==r)return l; int m=l+r>>1; if(k<=sum1[rt<<1])return query(k,lson); else return query(k-sum1[rt<<1],rson); } int main(){ cin>>n; for(int i=1;i<=n;i++)scanf("%d",&p[i].v),p[i].pos=i; sort(p+1,p+1+n,cmp); ll rev=0; for(int i=1;i<=n;i++){ update(p[i].pos,1,n,1); //二分找到第一个中点 int ans=query(i/2+i%2,1,n,1); ll Sum1=query2(1,ans-1,1,n,1);//ans前面的和 ll Sum2=query2(ans+1,n,1,n,1);//ans后面的和 ll cnt1=query1(1,ans-1,1,n,1);//ans前面个数 ll cnt2=query1(ans+1,n,1,n,1);//ans后面个数 ll cost=(Sum2-ans*cnt2)+(ans*cnt1-Sum1); if (cnt2)cost-=cnt2*(cnt2+1)/2; if (cnt1)cost-=(cnt1+1)*cnt1/2; rev+=query1(p[i].pos+1,n,1,n,1); cout<<cost+rev<<" "; } puts(""); }
cf1268C——线段树,逆序对
猜你喜欢
转载自www.cnblogs.com/zsben991126/p/12132787.html
今日推荐
周排行