【bzoj2141】【排队】【树状数组套平衡树】

本文介绍了一种用于计算和调整幼儿园小朋友排队顺序的算法。通过交换小朋友的位置来最小化队伍的杂乱程度,并提供了一个具体的实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

排排坐,吃果果,生果甜嗦嗦,大家笑呵呵。你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家乐和和。红星幼儿园的小朋友们排起了长长地队伍,准备吃果果。不过因为小朋友们的身高有所区别,排成的队伍高低错乱,极不美观。设第i个小朋友的身高为hi,我们定义一个序列的杂乱程度为:满足ihj的(i,j)数量。幼儿园阿姨每次会选出两个小朋友,交换他们的位置,请你帮忙计算出每次交换后,序列的杂乱程度。为方便幼儿园阿姨统计,在未进行任何交换操作时,你也应该输出该序列的杂乱程度。

Input

第一行为一个正整数n,表示小朋友的数量;第二行包含n个由空格分隔的正整数h1,h2,…,hn,依次表示初始队列中小朋友的身高;第三行为一个正整数m,表示交换操作的次数;以下m行每行包含两个正整数ai和bi¬,表示交换位置ai与位置bi的小朋友。

Output

输出文件共m行,第i行一个正整数表示交换操作i结束后,序列的杂乱程度。

Sample Input

【样例输入】
3
130 150 140
2
2 3
1 3

Sample Output

1
0
3
【样例说明】
未进行任何操作时,(2,3)满足条件;
操作1结束后,序列为130 140 150,不存在满足ihj的(i,j)对;
操作2结束后,序列为150 140 130,(1,2),(1,3),(2,3)共3对满足条件的(i,j)。
【数据规模和约定】

对于100%的数据,1≤m≤2*103,1≤n≤2*104,1≤hi≤109,ai≠bi,1≤ai,bi≤n。

题解:

和动态逆序对那个题基本一样.

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define N 20010
using namespace std;
int root[N],sz,ls[N*20],rs[N*20],n,m,x,y,t[N],a[N],h[N],ans,temp;
struct treap{int v,w,sum,rd;}tr[N*20];
int find(int x){
  int l=1,r=n;
  while (l<=r){
  	int mid=(l+r)>>1;
  	if (x<h[mid]) r=mid-1;
  	else if (x>h[mid]) l=mid+1;
  	else return mid;
  }
}
int lowbit(int x){return x&(-x);}
void ins(int x){for(int i=x;i<=n;i+=lowbit(i)) t[i]++;}
int que(int x){
  int ans(0);
  for (int i=x;i;i-=lowbit(i)) ans+=t[i];
  return ans;
}
void update(int k){
  tr[k].sum=tr[ls[k]].sum+tr[rs[k]].sum+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].w=tr[k].sum=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 getmn(int k,int v){
  if (!k) return;
  if (tr[k].v==v) temp+=tr[ls[k]].sum;
  else if (v<tr[k].v) getmn(ls[k],v);
  else {temp+=tr[ls[k]].sum+tr[k].w;getmn(rs[k],v);}	
}
void getmx(int k,int v){
  if (!k) return;
  if (tr[k].v==v){temp+=tr[rs[k]].sum;}
  else if (v>tr[k].v) getmx(rs[k],v);
  else{temp+=tr[rs[k]].sum+tr[k].w;getmx(ls[k],v);}
}
int quemn(int x,int v){
  temp=0;
  for (int i=x;i;i-=lowbit(i)) getmn(root[i],v);
  return temp;
}
int quemx(int x,int v){
  temp=0;
  for (int i=x;i;i-=lowbit(i)) getmx(root[i],v);
  return temp;
}
void add(int x,int v){for (int i=x;i<=n;i+=lowbit(i)) insert(root[i],v);}
void qdel(int x,int v){for (int i=x;i<=n;i+=lowbit(i)) del(root[i],v);}
int main(){
  scanf("%d",&n);
  for (int i=1;i<=n;i++) scanf("%d",&a[i]),h[i]=a[i];
  sort(h+1,h+n+1);
  for (int i=1;i<=n;i++) a[i]=find(a[i]);	
  for (int i=n;i>=1;i--){
  	ins(a[i]);ans+=que(a[i]-1);
  }
  printf("%d\n",ans);
  for (int i=1;i<=n;i++) add(i,a[i]);
  scanf("%d",&m);
  for (int i=1;i<=m;i++){
  	scanf("%d%d",&x,&y);
  	if (x>y) swap(x,y);if (a[x]==a[y]) {printf("%d\n",ans);continue;}
  	if (x+1==y){
  	  if (a[x]<a[y]) ans++;else ans--;
	  qdel(x,a[x]);add(x,a[y]);qdel(y,a[y]);add(y,a[x]);
	  swap(a[x],a[y]);printf("%d\n",ans);continue;
  	}
  	int r=y-1;
  	ans-=quemn(r,a[x])-quemn(x,a[x]);
	ans+=quemx(r,a[x])-quemx(x,a[x]);
    ans-=quemx(r,a[y])-quemx(x,a[y]);
    ans+=quemn(r,a[y])-quemn(x,a[y]);
    if (a[x]<a[y]) ans++;else ans--;
    qdel(x,a[x]);add(x,a[y]);
    qdel(y,a[y]);add(y,a[x]);
    swap(a[x],a[y]);printf("%d\n",ans);
  }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值