bzoj 4240: 有趣的家庭菜园 树状数组+贪心

有一个小性质:就是一个下标排列的最小移动次数就是逆序对数. 

我们发现最终形态一定是一个波峰.

 那么我们求的就是形成波峰的下标最少逆序对数.

考虑将元素从小到大依次插入. 

那么,对于第 $i$ 个元素,一定是放到左面或右面(就是从 1....left 或 right....n) 中的left和right.  

那么,考虑一个元素新放进去会对后面元素产生的逆序对:

放左:后面插入的下标比当前下标小的. 

放右:后面插入的下标比当前下标大的. 

由于我们在每次插入时已经计算完了插入元素和后面要插入元素的逆序对数. 

所以新插入时无需考虑与先前数字的逆序对数. 

所以每一步就贪心取最小就行了. 

一个细节:相同大小元素要同时插入,否则会多算. 

code:  

#include <bits/stdc++.h>  
#define N 300005      
#define LL long long 
using namespace std;  
void setIO(string s) 
{
    string in=s+".in";  
    string out=s+".out"; 
    freopen(in.c_str(),"r",stdin);  
    // freopen(out.c_str(),"w",stdout); 
}     
struct node 
{
    int h,pos; 
}a[N];     
int n;  
int A[N],C[N];                           
bool cmp(node a,node b) { return a.h<b.h; }    
int lowbit(int t) { return t&(-t); } 
void update(int x,int v) 
{
    while(x<N) C[x]+=v,x+=lowbit(x);  
}
int query(int x) 
{
    int re=0; 
    while(x>0) re+=C[x],x-=lowbit(x);  
    return re;       
}
int main() 
{ 
    // setIO("input");  
    LL ans=0ll; 
    int i,j;       
    scanf("%d",&n);       
    for(i=1;i<=n;++i) scanf("%d",&a[i].h),a[i].pos=i,A[i]=a[i].h;  
    sort(A+1,A+1+n);                
    sort(a+1,a+1+n,cmp); 
    for(i=1;i<=n;++i) a[i].h=lower_bound(A+1,A+1+n,a[i].h)-A, update(i,1);     
    for(i=1;i<=n;i=j) 
    { 
        for(j=i;a[j].h==a[i].h;++j) update(a[j].pos,-1);    
        for(j=i;a[j].h==a[i].h;++j) 
        { 
            int re=query(a[j].pos);         
            ans+=1ll*min(re, query(n)-re);   
        }
    }        
    printf("%lld\n",ans);     
    return 0; 
}

  

 

posted @ 2019-10-29 16:32  EM-LGH  阅读(137)  评论(0编辑  收藏  举报