选拔赛B题可怜的ljb


在这里插入图片描述

Sample Input 1
2
5
5 4 2 1 3
2 5 4 1 3
4
1 2 3 4
1 2 4 3
Sample Output 1 2
1
Hint 第一组样例解释:有两对同学的相对顺序改变了,分别是:(5,2),(4,2)。因为第一轮5排在2前面,但是第二轮2排在5前面,(4,2)同理,故答案是2.第二组样例解释:组数据有一对同学的相对顺序改变了,分别是:(3,4)。因为第一轮3排在4前面,但是第二轮4排在3前面,而且仅有这一对,故答案是1.

题解:这道题就是线段树求逆序对模板题,线段树求逆序对的精髓在于将每个数字作为自身位置放入线段树中,而放入是有顺序的,数字本身是由顺序。由于数字自身大小与放入顺序所以才有这种良好的算法,比如说先放入2,再放入5,那么5天然大于2,放入线段树5中计算5-n中的数字会发现2不在其中那么逆序数为0。如果放入5再放入2那么在2-n中会发现5所以逆序数为1
现在看看这道。首先一开始有一个顺序,后来又编了一个顺序,问变了多少对跟逆序对很像。但是有一点一开始的顺序不是自然顺序,所以还不能直接套模板。如果我们能够将初始顺序便自然顺序,那么就可以直接套模板了,所以我们定义数组,a【5】=1 a【4】=2等这样就自然记录了其顺序,而下一次输入顺序时,我们找到相应数组里的值,当作位置输入线段树,在进行查询这样就可以得到相应的改编

顺序数(也就是逆序数之前的)
以下为代码

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
int n,m;
const int maxn=100005;
typedef long long ffi;
ffi node[maxn<<2];
void pushup(int nd)
{
    node[nd]=node[nd<<1]+node[nd<<1|1];
}
void build(int l,int r,int nd)
{
    if(l==r)
    {
        node[nd]=0;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,nd<<1);
    build(mid+1,r,nd<<1|1);
    pushup(nd);
}
void update(int pos,int l,int r,int nd)
{
    if(l==r)
    {
        node[nd]=1;
        return;
    }
    int mid=(l+r)>>1;
    if(mid<pos)update(pos,mid+1,r,nd<<1|1);
    else update(pos,l,mid,nd<<1);
    pushup(nd);
}
ffi query(int s,int e,int l,int r,int nd)
{
    if(s<=l&&r<=e)
    {
        return node[nd];
    }
    ffi ans=0;
    int mid=(l+r)>>1;
    if(s<=mid)ans+=query(s,e,l,mid,nd<<1);
    if(e>mid)ans+=query(s,e,mid+1,r,nd<<1|1);
    return ans;
}
int main()
{
    int t;cin>>t;
    while(t--)
    {
        int n;cin>>n;
        build(1,n,1);
        int k;ffi a[maxn];
        for(int i=1;i<=n;i++)
        {
            cin>>k;
            a[k]=i;
        }
        ffi sum=0;
        for(int i=1;i<=n;i++)
        {
            cin>>k;
            sum+=query(a[k],n,1,n,1);
            update(a[k],1,n,1);
        }
        cout<<sum<<endl;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_43331783/article/details/88357907
今日推荐