Bzoj4989 [Usaco2017 Feb]Why Did the Cow Cross the Road

时间限制:1S / 空间限制:256MB

【在线测试提交传送门】

【问题描述】

    Why did the cow cross the road? We may never know the full reason, but it is certain that Farmer John's cows do end up crossing the road quite frequently. In fact, they end up crossing the road so often that they often bump into each-other when their paths cross, a situation Farmer John would like to remedy.
Farmer John raises N breeds of cows (1≤N≤100,0001), and each of his fields is dedicated to grazing for one specific breed; for example, a field dedicated to breed 12 can only be used for cows of breed 12 and not of any other breed. A long road runs through his farm. There is a sequence of N fields on one side of the road (one for each breed), and a sequence of N fields on the other side of the road (also one for each breed). When a cow crosses the road, she therefore crosses between the two fields designated for her specific breed.

Had Farmer John planned more carefully, he would have ordered the fields by breed the same way on both sides of the road, so the two fields for each breed would be directly across the road from each-other. This would have allowed cows to cross the road without any cows from different breeds bumping into one-another. Alas, the orderings on both sides of the road might be different, so Farmer John observes that there might be pairs of breeds that cross. A pair of different breeds (a,b)is "crossing" if any path across the road for breed aa must intersect any path across the road for breed b.

Farmer John would like to minimize the number of crossing pairs of breeds. For logistical reasons, he figures he can move cows around on one side of the road so the fields on that side undergo a "cyclic shift". That is, for some 0≤k<N, every cow re-locates to the field kk fields ahead of it, with the cows in the last kk fields moving so they now populate the first kk fields. For example, if the fields on one side of the road start out ordered by breed as 3, 7, 1, 2, 5, 4, 6 and undergo a cyclic shift by k=2, the new order will be 4, 6, 3, 7, 1, 2, 5. Please determine the minimum possible number of crossing pairs of breeds that can exist after an appropriate cyclic shift of the fields on one side of the road.
  有两个由1到n整数组成的全排列A和全排列B。将两个序列分成上下两行,A序列在第一行,B序列在第二行,当A序列的第i个元素和B序列的第j个元素相等时,两个元素间连接一条线段。
  你可以选择其中的一个序列,进行k不旋转,例如,对于序列3, 7, 1, 2, 5, 4, 6,当k=2时,将各个元素依次向右选择2位,结果为 4, 6, 3, 7, 1, 2, 5。
   选择其中一个序列旋转k步,求最小的线段相交的数量。

【输入格式】

The first line of input contains N. 
The next N lines describe the order, by breed ID, of fields on one side of the road; each breed ID is an integer in the range 1…N. 
The last N lines describe the order, by breed ID, of the fields on the other side of the road.
第一行一个整数N;
接下来N行,每行一个整数,表示序列A的各个元素;
接下来N行,每行一个整数,表示序列B的各个元素。

【输出格式】

Please output the minimum number of crossing pairs of breeds after a cyclic shift of the fields on one side of the road (either side can be shifted).
输出共一行,一个整数,表示最小的线段相交数量。

【输入样例1】

5
5
4
1
3
2
1
3
2
5
4

【输出样例1】

0

【解题思路】

首先若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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值