[bzoj]3295: [Cqoi2011]动态逆序对 主席树

Description

对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

Input

输入第一行包含两个整数nm,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。
 

Output

 
输出包含m行,依次为删除每个元素之前,逆序对的个数。

Sample Input

5 4
1
5
3
4
2
5
1
4
2

Sample Output

5
2
2
1

样例解释
(1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。

HINT

N<=100000 M<=50000



玩个蛋,高高兴兴写了个主席树,MLE到死,最后发现要优化才行。

因为n>m,所以我们得考虑将nlognlogn搞成mlognlogn,特别蛋疼。

对于原序列,建议棵主席树(不带修改的那种,重复使用信息的),然后对于后来修改的,再建一棵主席树,蛋疼。

这题就是询问1---x-1,x+1---n,分别有多少个大于他与小于他的个数,维护一下就是了。

萎靡的主席树(我要写分块儿!!)


#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<ctime>
using namespace std;
#define MAX_N 208500
typedef long long LL;
LL  bit[MAX_N];
LL  ans=0;

int n,m;
int bel[MAX_N];
void addans(int x){
	for (;x<=n;x+=x&(-x)) bit[x]++;
}
LL getans(int x){
	LL ret=0;
	for (;x>=1;x-=x&(-x)) ret+=bit[x];
	return ret;
}
int lc[MAX_N*45],rc[MAX_N*45],root[MAX_N*45],sum[MAX_N*45];
int tal=0;
void updata(int k){
	sum[k]=sum[lc[k]]+sum[rc[k]];
}
void add(int &k,int l,int r,int x,int v){
	if (!k) k=++tal;
	if (l==r){
		sum[k]+=v;
		return;
		}
	int mid=(l+r)/2;
	if (x<=mid)	add(lc[k],l,mid,x,v);
		else  add(rc[k],mid+1,r,x,v);
	updata(k);
}
int v1[MAX_N/2],v2[MAX_N/2],tp1,tp2;
LL querymore(int l,int r,int x){
	LL ret=0;
	while (l<r){
		int mid=(l+r)/2;
		if (mid+1<=x){
			for (int k=1;k<=tp1;k++) v1[k]=rc[v1[k]];
			for (int k=1;k<=tp2;k++) v2[k]=rc[v2[k]];
			l=mid+1;
			}
		else{
			for (int k=1;k<=tp1;k++) ret-=sum[rc[v1[k]]];
			for (int k=1;k<=tp2;k++) ret+=sum[rc[v2[k]]];
			for (int k=1;k<=tp1;k++) v1[k]=lc[v1[k]];
			for (int k=1;k<=tp2;k++) v2[k]=lc[v2[k]];
			r=mid;
			}
//		printf("L:%d R:%d ret:%d x:%d\n",l,r,ret,x);
		}
	return ret;
}
LL queryless(int l,int r,int x){
	LL ret=0;
	while (l<r){
		int mid=(l+r)/2;
		if (mid+1<=x){
			for (int k=1;k<=tp1;k++) ret-=sum[lc[v1[k]]];
			for (int k=1;k<=tp2;k++) ret+=sum[lc[v2[k]]];
			for (int k=1;k<=tp1;k++) v1[k]=rc[v1[k]];
			for (int k=1;k<=tp2;k++) v2[k]=rc[v2[k]];
			l=mid+1;
			}
		else{
			for (int k=1;k<=tp1;k++) v1[k]=lc[v1[k]];
			for (int k=1;k<=tp2;k++) v2[k]=lc[v2[k]];
			r=mid;
			}
//		printf("L:%d R:%d ret:%d x:%d\n",l,r,ret,x);
		}
	return ret;
}
int main(){
	freopen("3295.in","r",stdin);
	freopen("3295.out","w",stdout);
	
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++){//树状数组->总共_逆序对 
		int x;
		scanf("%d",&x);
		ans+=getans(n)-getans(x);
		addans(x);
		bel[x]=i;
		for (int k=i;k<=n;k+=k&(-k))
			add(root[k],1,n,x,1);
		}
//	printf("ans:%d",ans);
	

	for (int i=1;i<=m;i++){
		printf("%I64d\n",ans);
		
		int x;
		scanf("%d",&x);
		int L,R;
		LL  ret1=0,ret2=0;
		L=1;R=bel[x];//K大 
		tp1=0;tp2=0;
		for (int k=L-1;k>=1;k-=k&(-k))
			v1[++tp1]=root[k];
		for (int k=R;k>=1;k-=k&(-k))
			v2[++tp2]=root[k];
		ret1+=querymore(1,n,x);
		
		L=bel[x];R=n;
		tp1=0;tp2=0;
		for (int k=L-1;k>=1;k-=k&(-k))
			v1[++tp1]=root[k];
		for (int k=R;k>=1;k-=k&(-k))
			v2[++tp2]=root[k];
		ret2+=queryless(1,n,x);
		
		for (int k=bel[x];k<=n;k+=k&(-k))
			add(root[k],1,n,x,-1);
//		printf("x:%d more:%d less:%d\n",x,ret1,ret2);
		ans-=ret1+ret2;
		}
	

	return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值