Codeforce 785E(分块)

链接:点击打开链接

题意:给出一个1~n的序列,有m次变换,每次交换两个元素的位置,求每次交换后的逆序数

代码:

#include <math.h>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
vector<int> G[10005];
int n,m,siz,a[200005];
int le[200005],re[200005],bel[200005];
void init(){                                    //bel[i]为i属于第几块
    int i,id,tmp;                               //le[i]为第i块左端点的位置
    tmp=sqrt(n);                                //re[i]为第i块右端点的位置
    siz=n/tmp;                                  //G[i]维护第i块内的元素   
    if(n%tmp)
    siz++;
    for(i=0;i<=siz;i++)
    G[i].clear();
    for(i=1;i<=n;i++){
        a[i]=i;
        id=(i-1)/tmp+1;
        bel[i]=id;
        G[id].push_back(i);
    }
    for(i=1;i<=siz;i++)
    le[i]=(i-1)*tmp+1,re[i]=i*tmp;
    re[siz]=n;
}
void update(int u,int v){                       //维护每个快内有序,从而才能在查询时二分
    int idu,idv;
    idu=bel[u],idv=bel[v];
    G[idu].erase(lower_bound(G[idu].begin(),G[idu].end(),a[u]));
    G[idu].insert(upper_bound(G[idu].begin(),G[idu].end(),a[v]),a[v]);
    G[idv].erase(lower_bound(G[idv].begin(),G[idv].end(),a[v]));
    G[idv].insert(upper_bound(G[idv].begin(),G[idv].end(),a[u]),a[u]);
    swap(a[u],a[v]);
}
int query(int ll,int rr,int x){
    if(ll>rr)
    return 0;
    int i,j,ans,idl,idr;
    ans=0;
    idl=bel[ll],idr=bel[rr];
    if(idl==idr){                               //同块直接暴力找
        for(i=ll;i<=rr;i++)
        if(a[i]>x)
        ans++;
        return ans;
    }                                           
    for(i=ll;i<=re[idl];i++)                    //边界也是暴力找
    if(a[i]>x)  
    ans++;
    for(i=idl+1;i<=idr-1;i++)                   //因为每块单调上升所以每块内直接二分
    ans+=(G[i].size()-(lower_bound(G[i].begin(),G[i].end(),x)-G[i].begin()));
    for(i=le[idr];i<=rr;i++)
    if(a[i]>x)
    ans++;
    return ans;
}
int main(){                                     //分块后,边界直接暴力,快内二分
    long long ans;                              //复杂度又O(m*n*n)就变成了O(m*sqrt(n)*log(sqrt(n)))
    int i,j,u,v,tmp;
    while(scanf("%d%d",&n,&m)!=EOF){
        ans=0,init();
        while(m--){
            scanf("%d%d",&u,&v);
            if(u==v){
                printf("%I64d\n",ans);
                continue;
            }
            if(u>v)
            swap(u,v);
            tmp=query(u+1,v-1,a[u]);            //分别找出区间内大于a[u]和a[v]的数
            ans+=tmp;                           //再根据逆序数的定义推出变换后的值
            ans-=(v-u-1-tmp);
            tmp=query(u+1,v-1,a[v]);
            ans-=tmp;
            ans+=(v-u-1-tmp);
            if(a[u]>a[v])
            ans--;
            else
            ans++;
            printf("%I64d\n",ans);
            update(u,v);                        //查询完更新
        }
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值