Infinite Inversions(离散化+线段树求逆序对)

114 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

There is an infinite sequence consisting of all positive integers in the increasing order: p = {1, 2, 3, ...}. We performed n swap operations with this sequence. A swap(a, b) is an operation of swapping the elements of the sequence on positions a and b. Your task is to find the number of inversions in the resulting sequence, i.e. the number of such index pairs (i, j), that i < j and pi > pj.

Input

The first line contains a single integer n (1 ≤ n ≤ 105) — the number of swap operations applied to the sequence.

Each of the next n lines contains two integers ai and bi (1 ≤ ai, bi ≤ 109, ai ≠ bi) — the arguments of the swap operation.

Output

Print a single integer — the number of inversions in the resulting sequence.

Examples

input

Copy

2 4 2 1 4 output

Copy

4 input

Copy

3 1 6 3 4 2 5 output

Copy

15 Note

In the first sample the sequence is being modified as follows: image.png

. It has 4 inversions formed by index pairs (1, 4), (2, 3), (2, 4) and (3, 4).

翻译一下题意:在一个由所有正整数按递增顺序组成的无限序列中,我们进行n次操作,每次交换l和r上的两个数,求最后序列的逆序对个数

分析:这道题如果不是无限序列的话就是一个裸的求逆序对的题目,求逆序对的方法在这里我就不赘述了,如果有不懂的小伙伴可以看下我之前的博客。

我们不能直接讲需要交换的数离散化,因为不需要交换的数对逆序对的数目也会产生影响,所以我们需要把这些数考虑在内,为此我们可以把它们分块,注意块与块之间的大小关系是确定的,比如对于序列 1 2 3 4 5 6 7 8……,我们要交换3和6,我们就可以把1,2分成一块,3单独成一块,4和5分成一块,6单独一块,7及其之后的分成一块,每一块我们需要选择一个代表元素,不妨就选择这个块中最小的那个元素吧,反正代表元素并不会影响结果的正确性,我们记录下每块的大小,最后直接正常求逆序对就好了,下面是代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
const int N= 3e6+10;
long long l[N],r[N],siz[N],sum[N];
vector<int> alls;//离散化数组 
struct node{
	int l,r;
}p[N];
int ans[N];//储存离散化后的数 
int find(int x)
{
	//alls是待离散化数组 
	return (lower_bound(alls.begin(),alls.end(),x)-alls.begin()+1);
}
void pushup(int id)
{
	sum[id]=sum[id<<1]+sum[id<<1|1];
}
void build(int id,int L,int R)
{
	l[id]=L;r[id]=R;sum[id]=0;
	if(L==R) return ;
	int mid=L+R>>1;
	build(id<<1,L,mid);
	build(id<<1|1,mid+1,R);
	pushup(id); 
}
void update_point(int x,int id,int val)
{
	if(l[id]==r[id])
	{
		sum[id]+=val;
		return ;
	}
	int mid=l[id]+r[id]>>1;
	if(x<=mid) update_point(x,id<<1,val);
	else update_point(x,id<<1|1,val);
	pushup(id);
}
int query_interval(int id,int L,int R)
{
	if(l[id]>R||r[id]<L) return 0;
	if(l[id]>=L&&r[id]<=R) return sum[id];
	return query_interval(id<<1,L,R)+query_interval(id<<1|1,L,R);
}
int main()
{
	int n,t;
	cin>>n; 
	build(1,1,n*4);//建树时要把数与数之间的间隙考虑进去
	for(int i=1;i<=n;i++)
		{
			scanf("%d%d",&p[i].l,&p[i].r);
			alls.push_back(p[i].l);
			alls.push_back(p[i].r);
		}
	sort(alls.begin(),alls.end());//排序 
	alls.erase(unique(alls.begin(),alls.end()),alls.end());//去重 
	siz[1]=1;
	int cnt=1;
	int len=alls.size();
	for(int i=1;i<len;i++)
	{
		if(alls[i]-alls[i-1]==1)//两个待离散化数组中间没有间隔 
		{
			siz[++cnt]=1;
			continue;
		}
		siz[++cnt]=alls[i]-alls[i-1]-1;
		alls.push_back(alls[i-1]+1);
		siz[++cnt]=1;
	}
	sort(alls.begin(),alls.end());
	for(int i=1;i<=cnt;i++)
		ans[i]=find(alls[i-1]);
	for(int i=1;i<=n;i++)
		swap(ans[find(p[i].l)],ans[find(p[i].r)]);
	long long anss=0;
	for(int i=1;i<=cnt;i++)
	{
		anss+=query_interval(1,ans[i],cnt)*siz[ans[i]];
		update_point(ans[i],1,siz[ans[i]]);//在离散化后的位置上加上这个块的大小 
	} 
	printf("%lld\n",anss);
	return 0;
}