The 2018 JUST Collegiate Programming Contest C题 求逆序对

大致题意:给了两行数,相同的数相连,求有多少对相交线

思路:数组a[i]存第二行第i个数在第一行的下标,然后求逆序对数就好了。

以前不会求逆序对数,网上看了两种方法,一种是树状数组,一种类似递归的归并排序,复杂度都是O(nlogn)。

1.归并排序写法:C - Intersections -逆序数-归并排序

只要需要交换,则逆序对数+1,然后交换。(排序结果从小到大)

当前已经排好序的左右两段各自内部的逆序对数已经加在ans里面了,且左右两段已经有序,所以在进行下一层归并的时候,只需要当取右段中的元素进行归并时ans++。


#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 1008611
ll ans,n,x,t;
int a[maxn],rp[maxn];
void msort(int l,int r)
{
    if(l>=r)
        return ;
    int mid=(l+r)/2;
    msort(l,mid);
    msort(mid+1,r);
    int i=l,j=mid+1,k=l;
    while(i<=mid&&j<=r)
    {
        if(a[i]<=a[j])//正序
            rp[k++]=a[i++];
        else//逆序
        {
            rp[k++]=a[j++];
            ans+=mid-i+1;
        }
    }
    //归并
    while(i<=mid)
        rp[k++]=a[i++];
    while(j<=r)
        rp[k++]=a[j++];
    //归并结果放回a中
    for(int i=l; i<=r; i++)
        a[i]=rp[i];
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--)
    {
        ans=0;
        cin>>n;
        for(int i=1; i<=n; i++)
        {
            cin>>x;
            rp[x]=i;
        }
        for(int i=1; i<=n; i++)
        {
            cin>>x;
            a[i]=rp[x];
        }
        msort(1,n);
        cout<<ans<<endl;
    }
    return 0;
}

2.树状数组求逆序对:

关于树状数组求逆序对的问题,自己可以说是从零学了一遍树状数组。

参考:算法学习(二)——树状数组求逆序数

树状数组彻底入门,算法小白都看得懂的超详细解析

自己补题的时候还遇到一个坑点就是ans应该是long long,不然就WA,因为ans的最大值应该是\sum _{i=1}^ni (n=1e5)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n;
int a[maxn]; 
int bit[maxn];
int sum(int i){
	int s=0;
	while(i>0){
		s+=bit[i];
		i-=i&(-i);
	}
	return s;
}
void add(int i,int x){
	while(i<=n){
		bit[i]+=x;
		i+=i&(-i);
	}
}
int main(){
//	freopen("in.txt","r",stdin);
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int t;
	int temp;
	long long ans;
	cin>>t;
	while(t--){
		ans=0;
		memset(bit,0,sizeof(bit));
		cin>>n;
		//下标从1开始是因为树状数组的下标从1开始 
		for(int i = 1 ; i <= n ; i ++){
			cin>>temp;
			a[temp]=i;//元素出现在这个位置 
		}
		for(int i = 1 ; i <= n ; i ++){
			cin>>temp;
			//sum求出的是比当前元素小且已经在当前元素前出现过的元素个数 
			//i是指包括当前元素,已经出现的元素个数
			//所以对当前元素来说,逆序数就是已经在它前面出现的比它大的元素个数
			//暨 当前元素个数i-当前元素本身1-比当前元素小的元素个数sum(mp[temp]) 
			ans+=i-1-sum(a[temp]);
			add(a[temp],1);//这个位置已经出现过元素 
			//以上两条语句如果换位置则ans+=i-sum(mp[temp]); 
		}
		cout<<ans<<endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/JSLS_WFQ/article/details/89246990