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; 
}

  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值