BZOJ2141排队——分块+二维树状数组

Description
排排坐,吃果果,生果甜嗦嗦,大家笑呵呵。你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家
乐和和。红星幼儿园的小朋友们排起了长长地队伍,准备吃果果。不过因为小朋友们的身高有所区别,排成的队伍
高低错乱,极不美观。设第i个小朋友的身高为hi,我们定义一个序列的杂乱程度为:满足ihj的(i,j)数量。幼儿
园阿姨每次会选出两个小朋友,交换他们的位置,请你帮忙计算出每次交换后,序列的杂乱程度。为方便幼儿园阿
姨统计,在未进行任何交换操作时,你也应该输出该序列的杂乱程度。
Input
第一行为一个正整数n,表示小朋友的数量;
第二行包含n个由空格分隔的正整数h1,h2,…,hn,依次表示初始队列中小朋友的身高;
第三行为一个正整数m,表示交换操作的次数;
以下m行每行包含两个正整数ai和bi,表示交换位置ai与位置bi的小朋友。
1≤m≤210^3,1≤n≤2104,1≤hi≤109,ai≠bi,1≤ai,bi≤n。
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)


这道题显然是二维树状数组,交换的时候就查询两个点中间的值即可。唉,等等,怎么N这么大呀?开二维树状数组直接MLE飞起,那么怎么做呢?我们考虑分块优化
将小朋友每 ( n ) \sqrt(n) ( n)个分为一块,我们将每一块的下标看做二维树状数组里的 x x x,离散之后的值看做 y y y,那么这样树状数组的内存复杂度就是 n l o g n nlogn nlogn,也就可以了。
但是因为分块,所以操作会稍微复杂一些。如果两个点在同一个块里,那么直接暴力修改即可。
如果不同在同一块里,先把两边端点块里暴力修改,中间的块就可以直接用二维树状数组修改了。
可能会有同学不知道怎么维护,那这里顺带讲一下,会的可以直接跳过:先删去原来的数的贡献,再加上新进的数的贡献。对于 l l l位置,贡献是后面大于等于它的数的个数,而 r r r位置的贡献就是它前面小于等于它的点的个数。最后的答案就是总共的对数(也就是 n ∗ ( n − 1 ) / 2 n*(n-1)/2 n(n1)/2)减去目前的贡献。
#include<bits/stdc++.h>
#define MAXN 20005
using namespace std;
int read(){
    char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int n,m,block,cnt,tot,ans,num,bl[MAXN],sum[505][MAXN],pos[MAXN];
struct node{
    int num,id;
    bool operator<(const node x)const{
        return num<x.num;
    }
}a[MAXN];
int lowbit(int x){return x&-x;}
void add(int p,int x,int ad){
    for(int i=p;i<=num;i+=lowbit(i))
     for(int j=x;j<=cnt;j+=lowbit(j))
      sum[i][j]+=ad;
}
int getsum(int p,int x){
    int res=0;
    if(p*x<=0) return 0;
    for(int i=p;i;i-=lowbit(i))
     for(int j=x;j;j-=lowbit(j))
      res+=sum[i][j];
    return res;
}
int calc(int x,int y,int u,int v){
    if(x>y) return 0;
    return getsum(y,v)-getsum(y,u-1)-getsum(x-1,v)+getsum(x-1,u-1); 
}
int main()
{
    n=read();block=sqrt(n);tot=n*(n-1)/2;
    for(int i=1;i<=n;i++) a[i].num=read(),a[i].id=i;
    sort(a+1,a+1+n);a[0].num=-1;
    for(int i=1;i<=n;i++) 
      if(a[i].num!=a[i-1].num) pos[a[i].id]=++cnt;else pos[a[i].id]=cnt;
    for(int i=1;i<=n;i++) bl[i]=i/block+1;num=bl[n];
    for(int i=1;i<=n;i++){
        ans+=getsum(bl[i],pos[i]);add(bl[i],pos[i],1);
    }
    m=read();printf("%d\n",tot-ans);
    for(int i=1;i<=m;i++){
        int l=read(),r=read();if(l>r) swap(l,r);
        if(bl[l]==bl[r]){
            add(bl[l],pos[l],-1);add(bl[l],pos[r],1);
            add(bl[r],pos[r],-1);add(bl[r],pos[l],1);
            for(int j=l+1;j<=r-1;j++){
                if(pos[l]<=pos[j]) ans--;if(pos[r]<=pos[j]) ans++;
                if(pos[l]>=pos[j]) ans++;if(pos[r]>=pos[j]) ans--;
            }
            if(pos[l]<pos[r]) ans--;if(pos[l]>pos[r]) ans++;
            swap(pos[l],pos[r]);
        }
        else{
            for(int j=l+1;j<=n;j++){
                if(bl[j]!=bl[l]) break;
                if(pos[j]>=pos[l]) ans--;if(pos[j]>=pos[r]) ans++;
                if(pos[j]<=pos[r]) ans--;if(pos[j]<=pos[l]) ans++;
            }
            for(int j=r-1;j;j--){
                if(bl[j]!=bl[r]) break;
                if(pos[j]<=pos[r]) ans--;if(pos[j]<=pos[l]) ans++;
                if(pos[j]>=pos[l]) ans--;if(pos[j]>=pos[r]) ans++;
            }
            add(bl[r],pos[r],-1);add(bl[r],pos[l],1);
            add(bl[l],pos[l],-1);add(bl[l],pos[r],1);
            ans-=calc(bl[l]+1,bl[r]-1,pos[l],cnt);ans+=calc(bl[l]+1,bl[r]-1,pos[r],cnt);
            ans-=calc(bl[l]+1,bl[r]-1,1,pos[r]);ans+=calc(bl[l]+1,bl[r]-1,1,pos[l]);
            if(pos[l]<pos[r]) ans--;if(pos[l]>pos[r]) ans++;
            swap(pos[l],pos[r]);            
        }
        printf("%d\n",tot-ans);
    }
    return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值