CSU 2070 Seating Chart
题意
给出一个序列,求第二个序列的逆序数。
解题思路
如果直接暴力求解,肯定会TLE。队友是用归并排序过的,我重新写了遍线段树,算法复杂度是O(nlogn)。
对于使用线段树求逆序数,方法如下。
对于求逆序数,我们只需要求出每个数字的前面有几个比他大的数,最后再把这些数加起来就是逆序数。有了这样的思路,我们就可以通过一边构造线段树,一边通过查询求和,从而得到逆序数。至于如何构造这个线段树,首先我们先构建一个空树,然后我们再对序列里面的每一个元素进行遍历。最下面一层最终构造出来的是1-n这个序列。例如当前遍历到arr[i]节点,那么我们就将它构造在其值对应的节点,然后再更新所有包含它的区间。然后就是查找,我们只需要查找[arr[i]+1,n]这个区间有多少元素就行了,因为当前树上有的是arr[i]之前的元素,查询这个区间我们就知道到底有几个比它大。最后求得的查询的和就是逆序数。
代码
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<map>
using namespace std;
const int maxn = 1e5+5;
int tree[maxn<<2];
map<string,int> m;
void pushup(int rt)
{
tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
void update(int id,int l,int r,int rt)
{
if(l==r)
{
tree[rt]=1;
return;
}
int m=(l+r)>>1;
if(id<=m) update(id,l,m,rt<<1);
else update(id,m+1,r,rt<<1|1);
pushup(rt);
}
long long query(int L,int R,int l,int r,int rt)
{
if(L<=l&&R>=r) return tree[rt];
int m=(l+r)>>1;
long long ans=0;
if(L<=m) ans+=query(L,R,l,m,rt<<1);
if(R> m) ans+=query(L,R,m+1,r,rt<<1|1);
return ans;
}
int main()
{
// freopen("in.txt","r",stdin);
int n;
while(cin>>n&&n)
{
string str;
memset(tree,0,sizeof(tree));
for(int i=1; i<=n; i++)
{
cin>>str;
m[str]=i;
}
long long ans=0;
for(int i=1; i<=n; i++)
{
cin>>str;
update(m[str],1,n,1);
ans+=query(m[str]+1,n,1,n,1);
}
cout<<ans<<endl;
}
return 0;
}