bzoj4989 [Usaco2017 Feb]Why Did the Cow Cross the Road(树状数组求逆序对个数)

首先若a,b线段交叉且A[a]< A[b],则B[a]>B[b],若我们给A序列强制从小到大的标号,转换B序列保证符合原题。则线段交叉的对数,就是B序列中逆序对的个数。我们可以用树状数组nlogn求出。然后考虑循环移位,现在只考虑对B序列做循环移位(A序列同理,反过来做即可。),实质上就是对转换后的B序列做循环移位,我们每次只把最后一位挪到最前面,挪n-1次即可得到所有循环移位后的结果。考虑把最后一位x挪到第一位,对逆序对个数的影响是什么。因为操作之后x在第一位,所以所有比x小的都在他后面,构成了新的x-1个逆序对。原来比x大的现在都不再和x构成逆序对,所以少了(n-x)个以前的逆序对。而其他的位置没有变化。因此操作过后逆序对数的变化为x-1-(n-x)。这样我们只需用树状数组求出第一次的逆序对个数,以后的逆序对个数都可以O(1)得到,我们记录最小值即可。
tips:转A和转B得到的序列是不同的,必须做两次。

#include <cstdio>
#include <cstring>
#define N 100010
#define ll long long
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline ll min(ll x,ll y){return x<y?x:y;}
int n,a[N],b[N],c[N];
ll ans=0;
inline int lowbit(int x){return x&(-x);}
inline void insert(int x){
    for(;x<=n;x+=lowbit(x)) c[x]+=1;
}
inline ll query(int x){
    ll res=0;
    for(;x>0;x-=lowbit(x)) res+=c[x];
    return res;
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();
    for(int i=1;i<=n;++i){
        int x=read();b[x]=i;
    }
    for(int i=1;i<=n;++i){int x=read();a[i]=b[x];}
    ll res=0;
    for(int i=n;i>=1;--i){
        res+=query(a[i]);insert(a[i]);
    }
    ans=res;
    for(int i=n;i>=2;--i) res+=a[i]-1-(n-a[i]),ans=min(ans,res);
    for(int i=1;i<=n;++i) b[a[i]]=i;
    res=0;memset(c,0,sizeof(c));//注意清零 
    for(int i=n;i>=1;--i){
        res+=query(b[i]);insert(b[i]);
    }
    ans=min(ans,res);
    for(int i=n;i>=2;--i) res+=b[i]-1-(n-b[i]),ans=min(ans,res);
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值