P1975 [国家集训队]排队

题目链接:P1975 [国家集训队]排队
题目大意: 给你一段数列,每次可以交换两个数的位置,问每次交换之后全局的逆序对个数。
题目分析: 对于一个序列,题目要求求动态的逆序对个数,考虑交换某两个位置,假设分别为 p o s 1 , p o s 2 pos_1,pos_2 pos1,pos2,那么发生变化的逆序对一定是在 [ p o s 1 , p o s 2 ] [pos_1,pos_2] [pos1,pos2]这个区间上的,那么考虑交换,就等价于将 p o s 1 pos_1 pos1 位置的数字删去,将 p o s 2 pos_2 pos2 位置的数字删去,将 p o s 2 pos_2 pos2 位置加一个新数,将 p o s 1 pos_1 pos1位置加一个新数。因此考虑求区间排名,然后就可以离散化之后用树套树来解决了。
下面是代码 (写的丑轻喷)
题目代码:

#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
#define mxn 100005
int Rt,n,m,Tot,tot,arr[mxn],brr[mxn],Ns[mxn],a[mxn],rt[mxn*10],sm[mxn*300],ls[mxn*300],rs[mxn*300],Ls[mxn*10],Rs[mxn*10];

void Add(int &k,int L,int R,int p,int o){
	if(o==1&&!k)k=++tot;
	if(k)sm[k]+=o;
	if(!k||L==R)return;
	int mid=(L+R)>>1;
	if(p<=mid)Add(ls[k],L,mid,p,o);
	else Add(rs[k],mid+1,R,p,o);
}
int Query(int k,int L,int R,int l,int r){
	if(!k)return 0;
	if(l<=L&&r>=R)return sm[k];
	int mid=(L+R)>>1;
	if(r<=mid)return Query(ls[k],L,mid,l,r);
	else if(l>mid)return Query(rs[k],mid+1,R,l,r);
	else return Query(ls[k],L,mid,l,mid)+Query(rs[k],mid+1,R,mid+1,r);
}
int Query_rank(int k,int L,int R,int l,int r,int f,int t){
	if(!k)return 0;
	if(l<=L&&r>=R)return Query(rt[k],1,n,f,t);
	int mid=(L+R)>>1;
	return (l<=mid?Query_rank(Ls[k],L,mid,l,r,f,t):0)+(r>mid?Query_rank(Rs[k],mid+1,R,l,r,f,t):0);
}
int Query_kth(int k,int L,int R,int x,int l,int r){
	if(L==R)return L;
	int mid=(L+R)>>1,tmp=Query(rt[Ls[k]],1,n,l,r);
	if(x<=tmp)return Query_kth(Ls[k],L,mid,x,l,r);
	else return Query_kth(Rs[k],mid+1,R,x-tmp,l,r);
}
void Update(int &k,int L,int R,int v,int p,int o){
	if(o==1&&!k)k=++Tot;
	if(k)Add(rt[k],1,n,p,o);
	if(!k||L==R)return;
	int mid=(L+R)>>1;
	if(v<=mid)Update(Ls[k],L,mid,v,p,o);
	else Update(Rs[k],mid+1,R,v,p,o);
}
int Get_pre(int x,int y,int z){
	if(Query_rank(Rt,0,1e8,0,z-1,x,y)==Query_rank(Rt,0,1e8,0,-1e9,x,y))return -2147483647;
	return Query_kth(Rt,0,1e8,Query_rank(Rt,0,1e8,0,z-1,x,y),x,y);
}
int Get_nxt(int x,int y,int z){
	if(Query_rank(Rt,0,1e8,0,z,x,y)==Query_rank(Rt,0,1e8,0,1e9,x,y))return 2147483647;
	return Query_kth(Rt,0,1e8,Query_rank(Rt,0,1e8,0,z,x,y)+1,x,y);
}
int ans; 
void mergesort(int l,int mid,int r)
{
	int ll=l,rr=mid+1,ss=l;
	while(ss<=r){
		if(rr>r||arr[ll]<=arr[rr]&&ll<=mid) {
			brr[ss]=arr[ll];
			ll++; 
		}
		else{
			brr[ss]=arr[rr];
			ans+=mid-ll+1;
			rr++;
		}
		ss++; 
	}
	for(int i=l;i<=r;++i)
		arr[i]=brr[i];
}
void merge(int l,int r)
{
	if(l==r)return ;
	int mid=(l+r)/2;
	merge(l,mid);
	merge(mid+1,r);
	mergesort(l,mid,r);
} 
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&arr[i]);
		Ns[i]=arr[i];
	}
	sort(Ns+1,Ns+n+1);
	int N=unique(Ns+1,Ns+n+1)-Ns-1;
	for(int i=1;i<=n;i++)
		arr[i]=a[i]=lower_bound(Ns+1,Ns+N+1,arr[i])-Ns;
	for(int i=1;i<=n;i++)
		Update(Rt,0,1e9,arr[i],i,1);
	merge(1,n);
	for(int i=1;i<=n;i++)
		arr[i]=a[i];
	printf("%d\n",ans);
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		int a,b;
		scanf("%d %d",&a,&b);
		if(a>b)swap(a,b);
		if(arr[a]>arr[b])--ans;
		if(arr[a]<arr[b])++ans;
		if(b!=a+1){
			int len=b-a-1;
			int ns=Query_rank(Rt,0,1e9,0,arr[a]-1,a+1,b-1);//<a
			int np=Query_rank(Rt,0,1e9,0,arr[b]-1,a+1,b-1);//<b
			int nt=len-Query_rank(Rt,0,1e9,0,arr[a],a+1,b-1);//>a
			int nq=len-Query_rank(Rt,0,1e9,0,arr[b],a+1,b-1);//>b
			ans=ans-ns+np-nq+nt;
		}swap(arr[a],arr[b]);
		Update(Rt,0,1e9,arr[b],a,-1);
		Update(Rt,0,1e9,arr[b],b,1);
		Update(Rt,0,1e9,arr[a],b,-1);
		Update(Rt,0,1e9,arr[a],a,1);
		printf("%d\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值