杂讲002 一道小巧思的题目
P1966 [NOIP 2013 提高组] 火柴排队
这道题的思维相当之巧妙,首先,我们根据高中数学知识可以知晓 顺序乘积和>乱序乘积和,也就是排序不等式
那么,这道题就可以简单转化成一个调整顺序的问题,也就是,将两者的排名顺序调成一致。
先给出结论,就是这道题的答案就等于映射序列的逆序对数,下面来解释一下这个结论。
首先解释映射规则:a序列中每一个数都有一个排名序列,举个例子,比如现在n=4,a[4]=1 6 2 9,那么此时排序序列就是1 3 2 4,也就是该数列内值的排名重新整合为一个序列。我们要做的就是整理出a序列和b序列的排名序列,然后,重点来了,将a序列的排名的物理位置映射到b序列的排序序列上。用上方的数据做例子,比如说现在n=4,a[4]=1 6 2 9,b[4]=3 1 9 2,那么整理后的排名序列A[4]=1 3 2 4,B[4]=3 1 4 2,此时也就是以排名为纽带,将A的位置映射到B上,此时产生p数组,也就是p[4]=2 1 4 3这就是将A的排名的物理位置映射到B序列上得到的调整操作序列。此时,完成映射之后,可以将a序列的重排名序列视作1 2 3 4,p序列为b序列的重排名序列,我们要做的就是求p的逆序对数并输出。
下面解释一下为什么这样映射是合法的
首先,对于两个序列来说,我们要将他们的排名调整到相同的顺序,从而保证所求和最小,这就涉及到相对位置。直接思考怎么做到这种效果,最简单的就是直接将两个序列都排序一遍,显然这样操作是可以将排名顺序调整到一致的。然而,这样做在只能交换相邻位置的数的情况下复杂度是完全不可接受的,在这道题里也肯定不是最优策略。那么我们现在可以这样想,因为我们只能交换相邻位置的数,那么就存在一个绝对位置和相对位置的问题。绝对位置在本题中是不确定的,不能通过单一策略直接得到。但是我们思考可以知道,相对位置,也就是排名之间的顺序是有用的。如果两个排名之间的相对顺序(3在4前)是正确的,那我们如果将二者调换,就还是要调换回去,这样子操作显然会增加代价。所以我们可以得到一个很好的策略,就是无论如何变动,如果元素相对位置是正确的,就不调换两者的相对位置,保证他们始终不会相互调换位置,这样就能够降低调换位置的代价。那么这与上方的映射有什么关系呢?先考虑有序序列的条件,假设a是一个有序(正序)的序列,那么非常显然,根据我们上方策略,如果b序列的排名顺序符合正序(若排名3的元素在排名4的元素之前,就坚决不调换两者的位置),我们可以发现,这种情况下,显然,最终调换的答案就是b序列的逆序对数。尝试推广一下,假设a序列是一个乱序的序列,排名是不确定的,那么如果a中排名为5的元素在排名为4的元素前,那么如果b序列中排名为5的元素在排名为4的元素之前,就坚决不调换这两者元素的相对位置。由此,我们得到了一个通用的规则。就是“b序列顺从a序列”,如果b序列中排名的性质和a一样,就坚决不调换两者的相对位置。通过这一策略,我们假设现在a序列是一个正则的序列,也就是人为认定a序列的顺序一开始就是正确的,是正则的,所以我们就对b序列进行映射,求出b序列相对于a序列的相对位置序列,也就是上方映射规则得到的序列。对该序列求逆序对,就是最终答案。
#include<bits/stdc++.h> using namespace std; const int N=5e5+5,mod=1e8-3; long long ans=0,n,c[N]; void add(int x,int y,int n) { for(;x<=n;x+=(x&-x)) c[x]+=y; } long long query(int x) { long long ans=0; for(;x;x-=(x&-x)) ans+=c[x]; return ans; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin>>n; vector<int>a(n),b(n); for(int i=0;i<n;i++) cin>>a[i]; for(int i=0;i<n;i++) cin>>b[i]; vector<int>A=a;vector<int>B=b; sort(A.begin(),A.end());sort(B.begin(),B.end()); vector<int> ranka(n+1),rankb(n+1); for(int i=0;i<n;i++) { ranka[i]=int(lower_bound(A.begin(),A.end(),a[i])-A.begin())+1; rankb[i]=int(lower_bound(B.begin(),B.end(),b[i])-B.begin())+1; } vector<int>pos(n+1); for(int i=0;i<n;i++) pos[ranka[i]]=i+1; vector<int>p(n); for(int i=0;i<n;i++) p[i]=pos[rankb[i]]; for(int i=0;i<n;i++) { int left_rank=query(p[i]); ans+=i-left_rank; ans%=mod; add(p[i],1,n); } cout<<ans%mod; return 0; }

浙公网安备 33010602011771号