BZOJ4240: 有趣的家庭菜园
【传送门:BZOJ4240】
简要题意:
给出一个长度为n的序列,可以将相邻的数交换位置,要求通过最少交换次数使得这个序列呈左边段不递减,右边段不递增
题解:
树状数组+贪心
将每个数一开始的下标为原本的位置,最后得到的序列的逆序对数就是操作的次数
首先得到的序列肯定是最大的在中间,第二大的在旁边。。
那么就贪心放值,放的时候找左右两边能产生逆序对最少的放就可以了,用树状数组维护逆序对数
注意一下多个值相等的情况,且要加long long
参考代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> using namespace std; typedef long long LL; int a[310000]; int lowbit(int x){return x&-x;} int n; void change(int x,int d) { while(x<=n) { a[x]+=d; x+=lowbit(x); } } int getsum(int x) { int ans=0; while(x!=0) { ans+=a[x]; x-=lowbit(x); } return ans; } struct node { int id,d; }h[310000]; bool cmp(node n1,node n2) { return n1.d>n2.d; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&h[i].d); h[i].id=i; } sort(h+1,h+n+1,cmp); LL ans=0; for(int i=1;i<=n;) { int j=i-1; do { j++; int sum=getsum(h[j].id); ans+=min(sum,i-1-sum); }while(h[j+1].d==h[j].d); for(;i<=j;i++) change(h[i].id,1); } printf("%lld\n",ans); return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚