[蓝桥杯][历届试题]小朋友排队
只通过比较和交换相邻两个数值的排序方法,实际上就是冒泡排序。在排序过程中每找到一对大小颠倒的相邻数值,把它们交换,就会使整个序列的逆序对个数减少\(1\)。最终排好序后的逆序对个数显然为\(0\),所以对序列进行冒泡排序需要的最少交换次数就是序列中逆序对的个数。
本题要求计算出所有小朋友的不高兴程度之和,所有本题不是计算总的逆序对数,而是分别计算出每个位置上的数和序列中其他数所构成的逆序对数,从而得到每个位置上的不高兴程度,如果第\(i\)个位置和\([0 \sim i-1]\),\([i+1 \sim n-1]\)所构成的逆序对数为\(k\),则第\(i\)个位置的不高兴程度为\(1 + 2 + \cdots+k = \frac{k(k+1)}{2}\)。
归并排序:
const int N=1e5+10;
PII a[N],temp[N];
int cnt[N];
int n;
void merge(int l,int mid,int r)
{
int i=l,j=mid+1,k=0;
while(i<=mid && j<=r)
{
if(a[i].fi <= a[j].fi)
{
cnt[a[i].se]+=j-mid-1;
temp[k++]=a[i++];
}
else
{
cnt[a[j].se]+=mid-i+1;
temp[k++]=a[j++];
}
}
while(i<=mid)
{
cnt[a[i].se]+=j-mid-1;
temp[k++]=a[i++];
}
while(j<=r)
temp[k++]=a[j++];
for(int i=0;i<k;i++) a[l+i]=temp[i];
}
void mergeSort(int l,int r)
{
if(l >= r) return;
int mid=l+r>>1;
mergeSort(l,mid);
mergeSort(mid+1,r);
merge(l,mid,r);
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i].fi;
a[i].se=i;
}
mergeSort(0,n-1);
LL res=0;
for(int i=0;i<n;i++)
res+=(LL)cnt[i]*(cnt[i]+1)/2;
cout<<res<<endl;
//system("pause");
return 0;
}
树状数组:
const int N=1e6+10;
int c[N];
int a[N];
int cnt[N];
int n;
int lowbit(int x)
{
return x&-x;
}
void add(int x,int v)
{
for(int i=x;i<N;i+=lowbit(i))
c[i]+=v;
}
int sum(int x)
{
int res=0;
for(int i=x;i;i-=lowbit(i))
res+=c[i];
return res;
}
int main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>a[i],a[i]++;
// 求每个数前面有多少个数比它大
for(int i=0;i<n;i++)
{
cnt[i]+=sum(N-1)-sum(a[i]);
add(a[i],1);
}
memset(c,0,sizeof c);
// 求每个数后面有多少个数比它小
for(int i=n-1;i>=0;i--)
{
cnt[i]+=sum(a[i]-1);
add(a[i],1);
}
LL res=0;
for(int i=0;i<n;i++)
res+=(LL)cnt[i]*(cnt[i]+1)/2;
cout<<res<<endl;
//system("pause");
return 0;
}