【暴力+归并排序】[国家集训队]排队

题目

题目描述
排排坐,吃果果,生果甜嗦嗦,大家笑呵呵。你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家乐和和。

红星幼儿园的小朋友们排起了长长地队伍,准备吃果果。不过因为小朋友们的身高有所区别,排成的队伍高低错乱,极不美观。设第 ii 个小朋友的身高为 h_ih
i

幼儿园阿姨每次会选出两个小朋友,交换他们的位置,请你帮忙计算出每次交换后,序列的逆序对数。为方便幼儿园阿姨统计,在未进行任何交换操作时,你也应该输出该序列的逆序对数。

输入格式
第一行为一个正整数 nn,表示小朋友的数量;

第二行包含 nn 个由空格分隔的正整数 h_1,h_2,\dots,h_nh
1

,h
2

,…,h
n

,依次表示初始队列中小朋友的身高;

第三行为一个正整数 mm,表示交换操作的次数;

以下m行每行包含两个正整数 a_i,b_ia
i

,b
i

,表示交换位置 a_ia
i

和 b_ib
i

的小朋友。

输出格式
输出文件共 m+1m+1 行,第 ii 行一个正整数表示交换操作 ii 结束后,序列的逆序对数。

输入输出样例
输入 #1复制
3
130 150 140
2
2 3
1 3
输出 #1复制
1
0
3
说明/提示
【样例说明】
未进行任何操作时,(2,3)(2,3) 为逆序对;
操作一结束后,序列为 130 \ 140 \ 150130 140 150,不存在逆序对;
操作二结束后,序列为 150 \ 140 \ 130150 140 130,(1,2),(1,3),(2,3)(1,2),(1,3),(2,3) 共 33 个逆序对。

【数据范围】
对于 15%15 的数据,n,m \le 15n,m≤15;
对于 30%30 的数据,n,m \le 200n,m≤200;
另有 15%15% 的数据,h_ih
i

各不相同;
另有 15%15% 的数据,110 \le h_i \le 160110≤h
i

≤160;
以上两类数据交集为空。

对于100%的数据,1 \le m \le 2\times 10^31≤m≤2×10
3
,1 \le n \le 2 \times 10^41≤n≤2×10
4
,1 \le h_i \le 10^91≤h
i

≤10
9
,a_i \ne b_ia
i



=b
i

,1 \le a_i,b_i \le n1≤a
i

,b
i

≤n。

思路

观察发现,由于这道题的数据较小,其实可以用归并排序+暴力找逆序数对就行了,总复杂度为O(nlogn + nm),首先先用归并排序求出初始状态的逆序数对,然后在当交换两个数时暴力查找两数之间有多少逆序数对,进行减去或加上,然后直接输出答案。

代码

#include<bits/stdc++.h>
using namespace std;
int i,n,q,h[100002],o[100002],p[100002],ans,a,b;
int solve(int x,int y)
{
    int mid=(x+y)/2,j=x,k=mid+1,l=x;
    if(x==y) return 0;
    solve(x,mid);
    solve(k,y);
    while(j<=mid&&k<=y){
        if(o[j]<=o[k]){
            p[l]=o[j];
            j++;
            l++;
        }
        else{
            p[l]=o[k];
            ans+=mid-j+1;
            k++;
            l++;
        }
    }
    while(j<=mid){
        p[l]=o[j];
        j++;
        l++;
    }
    while(k<=y){
        p[l]=o[k];
        k++;
        l++;
    }
    for(i=x;i<=y;i++)
        o[i]=p[i];
    return 0;
}
int main()
{
    int j,k,l;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        scanf("%d",&h[i]);
    for(i=1;i<=n;i++)
        o[i]=h[i];
    solve(1,n);
    printf("%d\n",ans);
    scanf("%d",&q);
    for(i=1;i<=q;i++){
        scanf("%d%d",&a,&b);
        if(a>b){
            j=a;
            a=b;
            b=j;
        }
        if(h[b]>h[a])
            ans++;
        else if(h[b]<h[a])
            ans--;
        for(j=a+1;j<=b-1;j++){
            if(h[j]>h[a])
                ans++;
            else if(h[j]<h[a])
                ans--;
            if(h[j]<h[b])
                ans++;
            else if(h[j]>h[b])
                ans--;
        }
        j=h[a];
        h[a]=h[b];
        h[b]=j;
        printf("%d\n",ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值