大致题意:给了两行数,相同的数相连,求有多少对相交线
思路:数组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的最大值应该是
#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;
}