【bzoj3295】【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

题解:

          可以求出一开始的逆序对个数

          删除一个数相当于减少了这个数之前比这个数大的数和这个数之后比这个数小的数.

          这个可以用树套树来处理.

          线段树套平衡树应该是会T,改成树状数组套平衡树就可以了.

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define N 100010
using namespace std;
int a[N],root[N<<2],ls[N*30],rs[N*30],t[N],p[N],sz,n,m,tmp,x,tt;
long long ans;
struct treap{int s,w,rd,v;}tr[N*30];
int read(){
  int x(0);
  char ch=getchar();
  while (ch<'0'||ch>'9') ch=getchar();
  while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
  return x;
}
void add(int x){for (int i=x;i<=n;i+=i&(-i)) t[i]++;}
int que(int x){
  int ans(0);
  for (int i=x;i;i-=i&(-i)) ans+=t[i];
  return ans;
}
void update(int k){
  tr[k].s=tr[ls[k]].s+tr[rs[k]].s+tr[k].w;
}
void lturn(int &k){
  int t=rs[k];rs[k]=ls[rs[k]];ls[t]=k;
  update(k);update(t);k=t;
} 
void rturn(int &k){
  int t=ls[k];ls[k]=rs[ls[k]];rs[t]=k;
  update(k);update(t);k=t;
}
void insert(int &k,int v){
  if (!k){k=++sz;tr[k].s=tr[k].w=1;tr[k].v=v;tr[k].rd=rand();return;}
  if (tr[k].v==v) tr[k].w++;
  else if (v<tr[k].v){insert(ls[k],v);if (tr[ls[k]].rd<tr[k].rd) rturn(k);}
  else{insert(rs[k],v);if (tr[rs[k]].rd<tr[k].rd) lturn(k);}
  update(k); 
}
void del(int &k,int v){
  if (!k) return;
  if (tr[k].v==v){
    if (tr[k].w>1) tr[k].w--;
    else{
      if (ls[k]*rs[k]==0) k=ls[k]+rs[k];
	  else if (tr[ls[k]].rd<tr[k].rd){rturn(k);del(k,v);}
      else {lturn(k);del(k,v);}
    }
  }
  else if (v<tr[k].v) del(ls[k],v);
  else del(rs[k],v);
  update(k);
}
void build(int x,int v){for (int i=x;i<=n;i+=i&(-i)) insert(root[i],v);}
void after(int k,int v){
  if (!k) return;
  if (tr[k].v==v) tmp+=tr[rs[k]].s;
  else if (v<tr[k].v){tmp+=tr[k].w+tr[rs[k]].s;after(ls[k],v);}
  else after(rs[k],v);
}
void pre(int k,int v){
  if (!k) return;
  if (tr[k].v==v) tmp+=tr[ls[k]].s;
  else if (v<tr[k].v) pre(ls[k],v);
  else{tmp+=tr[k].w+tr[ls[k]].s;pre(rs[k],v);}
}
void getafter(int x,int v){
  for (int i=x;i;i-=i&(-i)) after(root[i],v);
}
void getpre(int x,int v){
  for (int i=x;i;i-=i&(-i)) pre(root[i],v);
}
void change(int x,int v){for (int i=x;i<=n;i+=i&(-i)) del(root[i],v);}
int main(){
  n=read();m=read();
  for (int i=1;i<=n;i++) a[i]=read(),p[a[i]]=i;
  for (int i=1;i<=n;i++) build(i,a[i]);
  for (int i=n;i>=1;i--){ans+=que(a[i]);add(a[i]);}
  for (int i=1;i<=m;i++){
    x=read();
    printf("%lld\n",ans);
    tmp=0;getafter(p[x],x);ans-=tmp;
    tmp=0;getpre(n,x);tt=tmp;
    tmp=0;getpre(p[x],x);ans-=tt-tmp;
	change(p[x],x);
  } 
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值