P2448 无尽的生命

题意

一个很长的序列 a1,a2,a3,,a2311a_1,a_2,a_3,…,a_{2^{31}-1},值分别是 1,2,3,,23111,2,3,…,2^{31}-1

进行 kk 个操作,每次有两个数 x,yx,y,请交换 ax,aya_x,a_y,最后输出新序列的逆序对数。

分析

每次交换只会改变两个数的位置,交换的次数也比较少,于是我们可以根据 x,yx,y 把整个序列分成几段,每段内的数字都是连续的。

请看下面的例子:

2
1 8
4 10

交换的点 x,yx,y 显然要单独一段,中间的数也要一段。

12 345 6 789101|2\ 3|4|5\ 6\ 7|8|9|10

这样就知道怎么分了吧,所有作为左端点的数就是 x,x+1,y,y+1x,x+1,y,y+1,将他们放进数组 cc 中进行排序并去重,确定每个区间的左端点,右端点就是下一个区间的左端点减 11。虽然这样看起来像是离散化,但其实并不是。交换时只要交换 x,yx,y 所在的段即可。

for(int i=1;i<=n;i++){
	x[i]=read();y[i]=read();
	c[++m]=x[i];c[++m]=x[i]+1;
	c[++m]=y[i];c[++m]=y[i]+1;
}
sort(c+1,c+m+1);
int tot=unique(c+1,c+m+1)-c-1;
for(int i=1;i<tot;i++)
	a[i].l=c[i],a[i].r=c[i+1]-1;
for(int i=1;i<=n;i++){
	x[i]=lower_bound(c+1,c+tot+1,x[i])-c;
	y[i]=lower_bound(c+1,c+tot+1,y[i])-c;
	swap(a[x[i]],a[y[i]]);
}

接下来就是求逆序对了。

按照套路,倒序扫描。

然后我们需要实现:

  • 求一段区间中少于某个数的数的个数——区间查询

  • 把一段连续的数出现的次数增加 11——区间增加

这不就是线段树吗!

于是我们可以以权值为节点建一棵线段树。

然而叶子节点数多达 23112^{31}-1,普通线段树是存不下的,所以我们用动态开点的线段树就可以了。

每次求答案记得要乘上区间的长度。

最后就是动态开点权值线段树板子了。

友情提示:要开 long long,动态开点线段树空间要开大。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll read(){
	ll x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
void write(ll x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
const int N=1e5+10,M=3e6+10;
int n,m,c[N<<2],x[N],y[N],t=1,l,r;
ll ans;
struct qj{
	int l,r;
}a[N<<2];
struct Tree{
	int cl,cr;
	ll tag,sum;
}s[M];
void make(int p){
	if(s[p].cl)
		return;
	s[p].cl=++t;s[p].cr=++t;
}
void pushup(int p){
	s[p].sum=s[s[p].cl].sum+s[s[p].cr].sum;
}
void pushdown(int p,int l,int mid,int r){
	if(s[p].tag){
		s[s[p].cl].tag=s[s[p].cr].tag=s[p].tag;
		s[s[p].cl].sum+=s[p].tag*(mid-l+1);
		s[s[p].cr].sum+=s[p].tag*(r-mid);
		s[p].tag=0;
	}
}
void add(int p,int l1,int r1){
	if(l<=l1&&r1<=r){
		s[p].tag++;
		s[p].sum+=(r1-l1+1);
		return;
	}
	int mid=(l1+r1)>>1;
	make(p);
	pushdown(p,l1,mid,r1);
	if(l<=mid)
		add(s[p].cl,l1,mid);
	if(r>mid)
		add(s[p].cr,mid+1,r1);
	pushup(p);
}
ll ask(int p,int l1,int r1){
	if(l<=l1&&r1<=r)
		return s[p].sum;
	ll ans=0;
	int mid=(l1+r1)>>1;
	make(p);
	pushdown(p,l1,mid,r1);
	if(l<=mid)
		ans+=ask(s[p].cl,l1,mid);
	if(r>mid)
		ans+=ask(s[p].cr,mid+1,r1);
	return ans;
}
int main()
{
    n=read();
	for(int i=1;i<=n;i++){
		x[i]=read();y[i]=read();
		c[++m]=x[i];c[++m]=x[i]+1;
		c[++m]=y[i];c[++m]=y[i]+1;
	}
	sort(c+1,c+m+1);
	int tot=unique(c+1,c+m+1)-c-1;
	for(int i=1;i<tot;i++)
		a[i].l=c[i],a[i].r=c[i+1]-1;
	for(int i=1;i<=n;i++){
		x[i]=lower_bound(c+1,c+tot+1,x[i])-c;
		y[i]=lower_bound(c+1,c+tot+1,y[i])-c;
		swap(a[x[i]],a[y[i]]);
	}
	for(int i=tot-1;i;i--){
		l=1;r=a[i].l-1;
		if(l<=r)
			ans+=ask(1,1,c[tot])*(a[i].r-a[i].l+1);
		l=a[i].l,r=a[i].r;
		add(1,1,c[tot]);
	}
	write(ans);
	return 0;
}
posted @ 2021-08-21 14:37  luckydrawbox  阅读(14)  评论(0)    收藏  举报  来源