树状数组——BZOJ4240 有趣的家庭菜园

http://www.lydsy.com/JudgeOnline/problem.php?id=4240
我们的4.12模拟赛T1
和找逆序对差不多。。。
把序列变成合唱队形差不多就行了
一开始的想法是直接左边逆序对数和右边正序对数合并找最小
这样只有10分QAQ。。。
正确的思路是直接找数i在前面的逆序对数和后面的正序对数取小累加
用树状数组维护一下,时间复杂度O(nlogn)

#include
using namespace std;
typedef long long ll;
inline ll read(){
    ll k=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){k=k*10+ch-'0';ch=getchar();}
    return k*f;
}
ll n,a[400001],b[400001],f[400001],c[400001];
ll m1[400001],m2[400001];
inline ll erfen(ll x,ll sum){
    ll l=1,r=sum;
    while(l<=r){
        ll mid=(l+r)>>1;
        if(b[mid]==x)return mid;
        if(b[mid]>x)r=mid-1;
        else l=mid+1;
    }
    return 0;
}
inline ll lowbit(ll x){return x&-x;}
inline void add(ll x,ll v){for(ll i=x;i<=n;i+=lowbit(i))f[i]+=v;}
inline ll sum(ll x){ll ans=0;for(ll i=x;i;i-=lowbit(i))ans+=f[i];return ans;}
int main()
{
    n=read();ll s=1;
    ll p=sqrt(n);
    for(ll i=1;i<=n;i++)c[i]=a[i]=read();
    sort(c+1,c+n+1);
    b[1]=c[1];
    for(ll i=2;i<=n;i++)if(c[i]!=c[i-1])s++,b[s]=c[i];
    for(ll i=1;i<=n;i++)a[i]=erfen(a[i],s);
    memset(f,0,sizeof f);ll ans=0;
    for(ll i=1;i<=n;i++){
        add(a[i],1);
        ans=i-sum(a[i]);m1[i]=ans;
    }
    memset(f,0,sizeof f);ans=0;
    for(ll i=n;i;i--){
        add(a[i],1);
        ans=n-i+1-sum(a[i]);m2[i]=ans;
    }
    ans=0;
    for(ll i=1;i<=n;i++)ans+=min(m1[i],m2[i]);
    printf("%lld",ans);
    return 0;
}

你可能感兴趣的:(线段树/树状数组)