题目描述:
排排坐,吃果果,生果甜嗦嗦,大家笑呵呵。你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家乐和和。
红星幼儿园的小朋友们排起了长长地队伍,准备吃果果。不过因为小朋友们的身高有所区别,排成的队伍高低错乱,极不美观。设第i个小朋友的身高为hi,我们定义一个序列的杂乱程度为:满足i<j且hi>hj的(i,j)数量。
幼儿园阿姨每次会选出两个小朋友,交换他们的位置,请你帮忙计算出每次交换后,序列的杂乱程度。为方便幼儿园阿姨统计,在未进行任何交换操作时,你也应该输出该序列的杂乱程度。
输入描述:
第一行为一个正整数n,表示小朋友的数量;
第二行包含n个由空格分隔的正整数h1,h2,…,hn,依次表示初始队列中小朋友的身高;
第三行为一个正整数m,表示交换操作的次数;
以下m行每行包含两个正整数ai和bi,表示交换位置ai与位置bi的小朋友。
输出描述:
输出文件共m+1行,第i行一个正整数表示交换操作i结束后,序列的杂乱程度。
输入样例:
3
130 150 140
2
2 3
1 3
输出样例:
1
0
3
核心思想:
动态开点的线段树套树状数组(树套树)。
树套树模板传送门
tsum[i] 表示满足以下条件的人的个数:
1、比第i个人靠前
2、比第i个人高
序列的杂乱程度ans为n个tsum[i]的和。
对于某个i,我们通过树套树以log级的复杂度得到tsum[i],那么总时间复杂度为m*n*logn,会T。
对于每次操作,要将x处的人和y处的人互换位置。(不妨假定x<y)
我们将序列根据x和y分成5部分:
[1,x-1],x,[x+1,y-1],y,[y+1,n]
这5部分特点如下:
1、交换x和y对部分1和部分5不会产生影响。
2、对于部分3内的任意一个i,在i之前的数只改变了一个(h[x]变为了h[y]),tsum[i]要么+1要么-1(当h[x]=h[y]时,tsum[i]不变)。处理每个i的复杂度为常量级O(1),部分3最多有n个数,复杂度为O(n)。
3、由于x处和y处的高度改变了,x处和y处受到了很大的影响,用树套树log级更新和查询。
初始建树的时间复杂度为n*logn。
总时间复杂度=n*logn+m*(n+logn)。
代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=2e4+20;
//tsum[i]表示位置在i之前且高度大于h[i]的人的个数
int n,h[N],tsum[N];
int cb,b[N];//用于离散
struct node{
int z;
node *zuo,*you;
node():z(0),zuo(NULL),you(NULL){}
};
node *root[N];
int lowbit(int x)
{
return x&(-x);
}
void change(node *&now,int l,int r,int k,int v)
{
if(now==NULL)now=new node();
now->z+=v;
if(l==k&&r==k)return;
int mid=(l+r)>>1;
if(k<=mid)
change(now->zuo,l,mid,k,v);
else
change(now->you,mid+1,r,k,v);
return;
}
int sum(node *&now,int l,int r,int x,int y)
{
if(now==NULL)return 0;
if(l==x&&r==y)
return now->z;
int mid=(l+r)>>1;
if(y<=mid)
return sum(now->zuo,l,mid,x,y);
if(x>mid)
return sum(now->you,mid+1,r,x,y);
return sum(now->zuo,l,mid,x,mid)+sum(now->you,mid+1,r,mid+1,y);
}
int getsum(int y,int l)
{
int ans=0;
for(int i=y;i>0;i-=lowbit(i))
ans+=sum(root[i],1,cb,l,cb);
return ans;
}
int getid(int x)
{
return lower_bound(b+1,b+cb+1,x)-b;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&h[i]);
b[i]=h[i];
}
sort(b+1,b+n+1);
cb=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++)
h[i]=getid(h[i]);
//初始建树
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j+=lowbit(j))
change(root[j],1,cb,h[i],1);
int m,x,y,ans=0;
scanf("%d",&m);
for(int i=2;i<=n;i++)
{
tsum[i]=getsum(i-1,h[i]+1);
ans+=tsum[i];
}
printf("%d\n",ans);
//m次操作
for(int i=0;i<m;i++)
{
scanf("%d%d",&x,&y);
//相等不必交换
if(h[x]==h[y])
{
printf("%d\n",ans);
continue;
}
if(x>y)
swap(x,y);
//更新[x+1,y-1]
int xi=min(h[x],h[y]),da=max(h[x],h[y]),d=h[x]>h[y]?-1:1;
for(int i=x+1;i<y;i++)
if(h[i]>=xi&&h[i]<da)
tsum[i]+=d;
//更新x处和y处
for(int j=x;j<=n;j+=lowbit(j))
change(root[j],1,cb,h[x],-1);
for(int j=y;j<=n;j+=lowbit(j))
change(root[j],1,cb,h[y],-1);
swap(h[x],h[y]);
for(int j=x;j<=n;j+=lowbit(j))
change(root[j],1,cb,h[x],1);
for(int j=y;j<=n;j+=lowbit(j))
change(root[j],1,cb,h[y],1);
tsum[x]=getsum(x-1,h[x]+1);
tsum[y]=getsum(y-1,h[y]+1);
//求和输出ans
ans=0;
for(int i=2;i<=n;i++)
ans+=tsum[i];
printf("%d\n",ans);
}
return 0;
}