洛谷P5047 [Ynoi2019 模拟赛] Yuno loves sqrt technology II(离线区间逆序对+莫队二次离线)

题目

给你一个长为n(1<=n<=1e5)的序列a(0<=ai<=1e9),

m(1<=m<=1e5)次询问,每次查询一个区间[l,r]的逆序对数,可离线。

思路来源

登录 - 洛谷

三道经典分块题的更优复杂度解法&[Ynoi2019模拟赛]题解 - 博客 - OldDriverTree的博客

值域分块入门__ChiFAN_的博客-CSDN博客

题解

莫队二次离线:

1. 普通的莫队求区间逆序对个数,是莫队+树状数组求逆序对,复杂度O(n\sqrt[]{n}logn)

插入/删除一个值v时,实际要求当前区间[l,r]内,比v大/小的数的个数①,用树状数组求

而①可以继续离线,转化为求[1,r]比v大/小的数的个数-[1,l-1]比v大/小的数的个数

共有O(n\sqrt[]{n})个莫队询问,O(n)个前缀离线修改,

所以,需要一个O(\sqrt[]{n})的插入,O(1)的查询的数据结构,这就是值域分块

扫描二维码关注公众号,回复: 15238571 查看本文章

a[i]表示i位置的值,普通的莫队/分块都是按i分块,也就是按位置分块

而值域分块:按值的出现次数分块

cnt[i]:i这个值出现的次数,每\sqrt[]{n}个出现次数分一块,

这样就能实现O(\sqrt[]{n})的插入,O(1)的查询了

维护块内的个数的前缀和/后缀和,块间的个数的前缀和/后缀和,

插入时修改O(\sqrt[]{n})个块+块内位置,查询O(1)查

复杂度O(n\sqrt[]{n}+q\sqrt[]{n})

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int N=100005;
int n,m;ll a[N];
inline int lowbit(int x){return x&-x;}
ll c[N],sz=0;
int p[N];bool cmp(int x,int y){return a[x]<a[y];}
inline void add(int x,ll k){sz+=k;for(;x<=n;x+=lowbit(x))c[x]+=k;}
inline ll getsum(int x){ll ans=0;for(;x;x-=lowbit(x))ans+=c[x];return ans;}
ll L[N],R[N];//L[i]:1~i-1有多少个数大于a[i];R[i]:i+1~n有多少个数小于a[i] 

const int block=310;
struct ASK
{
    int l,r,p;
}ask[N];
inline bool mmp(ASK n1,ASK n2)
{
    if(n1.l/block!=n2.l/block)return n1.l<n2.l;
    if((n1.l/block)&1)return n2.r<n1.r;
    return n1.r<n2.r;
}
struct node{int l,r,p,op;};
vector<node>ls[N],rs[N];
int B,bl[N/block+5],br[N/block+5],w[N];
int s[N/block+5],C[N];

ll ret[N],ans[N];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        p[i]=i;
    }
    sort(p+1,p+n+1,cmp);
    ll tmp=a[p[1]];a[p[1]]=1;
    for(int i=2,t=1;i<=n;i++)
    {
        if(a[p[i]]!=tmp)t++;
        tmp=a[p[i]];a[p[i]]=t;
    }//离散化 

    for(int i=1;i<=n;i++)
    {
        L[i]=L[i-1]+sz-getsum(a[i]);
        add(a[i],1);//L[i]转前缀和 
    }//for(int i=1;i<=n;i++)printf("%lld ",L[i]);puts("");
    memset(c,0,sizeof(c));
    for(int i=n;i>=1;i--)
    {
        R[i]=R[i+1]+getsum(a[i]-1);
        add(a[i],1);//R[i]转后缀和 
    }//for(int i=1;i<=n;i++)printf("%lld ",R[i]);puts("");

    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&ask[i].l,&ask[i].r);
        if(ask[i].l>ask[i].r)swap(ask[i].l,ask[i].r);
        ask[i].p=i;
    }
    sort(ask+1,ask+m+1,mmp);//排序 
    ask[0]=(ASK){1,0,0};
    for(int i=1;i<=m;i++)//莫队二次离线 
    {//把莫队的移动离线下来 
        ret[i]=L[ask[i].r]-L[ask[i-1].r]+R[ask[i].l]-R[ask[i-1].l];
             if(ask[i].r>ask[i-1].r)rs[ask[i-1].l-1].push_back((node){ask[i-1].r+1,ask[i].r,i,-1});
        else if(ask[i].r<ask[i-1].r)rs[ask[i-1].l-1].push_back((node){ask[i].r+1,ask[i-1].r,i, 1});
             if(ask[i].l<ask[i-1].l)ls[ask[i  ].r+1].push_back((node){ask[i].l,ask[i-1].l-1,i,-1});
        else if(ask[i].l>ask[i-1].l)ls[ask[i  ].r+1].push_back((node){ask[i-1].l,ask[i].l-1,i, 1});
    }

    B=(n-1)/block+1;
    for(int i=1;i<=B;i++) 
    {
        bl[i]=br[i-1]+1;
        br[i]=br[i-1]+block;
    }br[B]=n;//值域分块 
    for(int i=1;i<=B;i++)for(int j=bl[i];j<=br[i];j++)w[j]=i;

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<w[a[i]];j++)s[j]++;
        for(int j=bl[w[a[i]]];j<=a[i];j++)C[j]++;
        for(int j=0;j<rs[i].size();j++)
        {
            node t=rs[i][j];
            int l=t.l,r=t.r;
            tmp=0;
            for(int k=l;k<=r;k++)tmp+=s[w[a[k]+1]]+C[a[k]+1];
            ret[t.p]+=t.op*tmp;
        }
    }
    memset(C,0,sizeof(C));
    memset(s,0,sizeof(s));
    for(int i=n;i>=1;i--)
    {
        for(int j=w[a[i]]+1;j<=B;j++)s[j]++;
        for(int j=a[i];j<=br[w[a[i]]];j++)C[j]++;
        for(int j=0;j<ls[i].size();j++)
        {
            node t=ls[i][j];
            int l=t.l,r=t.r;
            tmp=0;
            for(int k=l;k<=r;k++)tmp+=s[w[a[k]-1]]+C[a[k]-1];
            ret[t.p]+=t.op*tmp;
        }
    }

    for(int i=1;i<=m;i++)
    {
        ret[i]+=ret[i-1];//ret最终的值是差分值 
        ans[ask[i].p]=ret[i];
    }
    for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Code92007/article/details/130550996
今日推荐