peiwenjun's blog 没有知识的荒原

P5046 [Ynoi2019 模拟赛] Yuno loves sqrt technology I 题解

题目描述

给定一个长为 \(n\) 的排列, \(q\) 次询问区间逆序对数,强制在线。

数据范围

  • \(1\le n,q\le 10^5\)

时间限制 \(\texttt{750ms}\) ,空间限制 \(\texttt{512MB}\)

分析

分块,记 bel[i]\(i\) 所在块的编号, st[i],ed[i] 分别为块的开头,结尾。

预处理以下信息:

  • 块内 pair<a[i],i> 排序后的结果,记为 p[i]
  • 区间 [st[bel[i]],i] 的逆序对数,记为 pre[i]
  • 区间 [i,ed[bel[i]]] 的逆序对数,记为 suf[i]
  • 区间 [st[l],ed[r]] 的逆序对数,记为 f[l][r]
  • 区间 [1,ed[i]]\(\le j\) 的元素个数,记为 s[i][j]

其他都好求,单独将一下如何求f

注意到 f[l][r]=f[l][r-1]+f[l+1][r]-f[l+1][r-1]+ \([st_l,ed_l]\)\([st_r,ed_r]\) 的贡献,最后一项可以通过归并排序计算。

重点是如何回答询问。

分类讨论,记 x=bel[l],y=bel[r]

如果 \(x=y\) ,则 \(ans_{l,r}=ans_{st_x,r}-ans_{st_x,l-1}-[st_x,l-1]\)\([l,r]\) 的贡献。

\(2\) 项就是预处理的 pre 数组,最后一项用归并排序。为了得到 \([st_x,l-1]\)\([l,r]\) 排序后的数组,可以在整块排序后的数组上扫一遍。

如果 \(x\neq y\) ,拆分成 [l,ed[x]],[st[x+1],ed[y-1]],[st[y],r] 三个区间。

首先区间内部的贡献预处理过。

左散块对整块的贡献,以及整块对右散块的贡献,可以枚举散块中的元素,然后用 s 数组求出。

左散块对右散块的贡献,可以先扫一遍得到这两个数组排序后的结果,然后做归并排序。

时间复杂度 \(\mathcal O(n\sqrt n)\) ,但是非常卡常!

原本 merge 函数写成下面这样,但是过不去。

inline ll merge(int *a,int *b,int n,int m)
{
    ll res=0;
    for(int i=1,j=1;i<=n||j<=m;)
        if(i!=n+1&&(j==m+1||a[i]<=b[j])) i++;
        else res+=n-i+1,j++;
    return res;
}

但是改成这样就可以。

inline ll merge(int *a,int *b,int n,int m)
{
    ll res=0;
    for(int i=1,j=1;j<=m;j++)
    {
        while(i<=n&&a[i]<=b[j]) i++;
        res+=n-i+1;
    }
    return res;
}

以下是完整代码。

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
using namespace std;
const int b=600,sz=170,maxn=1e5+5;
int n,q;
int p[maxn],bel[maxn],cnt[maxn];
int c[maxn],u[sz+5],v[sz+5],tmp[maxn];
pii a[maxn];
int st[b],ed[b],len[b],s[b][maxn];
int pre[maxn],suf[maxn];
ll res,f[b][b];
inline ll read()
{
    ll q=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') q=10*q+ch-'0',ch=getchar();
    return q;
}
inline void write(ll x)
{
    if(x>=10) write(x/10);
    putchar(x%10+'0');
}
inline void add(int x,int v)
{
    while(x<=n) c[x]+=v,x+=x&(-x);
}
inline int sum(int x)
{
    int res=0;
    while(x) res+=c[x],x-=x&(-x);
    return res;
}
inline ll merge(int *a,int *b,int n,int m)
{
    ll res=0;
    for(int i=1,j=1;j<=m;j++)
    {
        while(i<=n&&a[i]<=b[j]) i++;
        res+=n-i+1;
    }
    return res;
}
inline int calc(int l,int r,int x,int opt)
{
    if(opt==0) return s[r][x]-s[l-1][x];
    return (s[r][n]-s[l-1][n])-(s[r][x]-s[l-1][x]);
}
int main()
{
    n=read(),q=read();
    for(int i=1;i<=n;i++) p[i]=read(),a[i]=mp(p[i],i),bel[i]=(i-1)/sz+1;
    for(int i=1;i<=bel[n];i++)
    {
        st[i]=(i-1)*sz+1,ed[i]=min(i*sz,n),len[i]=ed[i]-st[i]+1;
        for(int j=st[i];j<=ed[i];j++)
        {
            if(j!=st[i]) pre[j]=pre[j-1]+sum(n)-sum(p[j]);
            add(p[j],1);
        }
        for(int j=st[i];j<=ed[i];j++) add(p[j],-1);
        for(int j=ed[i];j>=st[i];j--)
        {
            if(j!=ed[i]) suf[j]=suf[j+1]+sum(p[j]-1);
            add(p[j],1);
        }
        for(int j=st[i];j<=ed[i];j++) add(p[j],-1);
        sort(a+st[i],a+ed[i]+1),f[i][i]=pre[ed[i]];
        for(int j=st[i];j<=ed[i];j++) tmp[j]=a[j].fi;
    }
    for(int k=2;k<=bel[n];k++)
        for(int l=1,r=k;r<=bel[n];l++,r++)
            f[l][r]=f[l][r-1]+f[l+1][r]-f[l+1][r-1]+
            merge(tmp+ed[l-1],tmp+ed[r-1],len[l],len[r]);
    for(int i=1;i<=bel[n];i++)
    {
        for(int j=st[i];j<=ed[i];j++) cnt[p[j]]++;
        for(int j=1;j<=n;j++) s[i][j]=s[i][j-1]+cnt[j];
    }
    while(q--)
    {
        int l=read()^res,r=read()^res,x=bel[l],y=bel[r],c0=0,c1=0;
        if(x==y)
        {
            if(l==st[x]) res=pre[r];
            else
            {
                for(int i=st[x];i<=ed[x];i++)
                    if(a[i].se<=l-1) u[++c0]=a[i].fi;
                    else if(a[i].se<=r) v[++c1]=a[i].fi;
                res=pre[r]-pre[l-1]-merge(u,v,c0,c1);
            }
        }
        else
        {
            res=suf[l]+f[x+1][y-1]+pre[r];
            for(int i=l;i<=ed[x];i++) res+=calc(x+1,y-1,p[i],0);
            for(int i=st[y];i<=r;i++) res+=calc(x+1,y-1,p[i],1);
            for(int i=st[x];i<=ed[x];i++) if(a[i].se>=l) u[++c0]=a[i].fi;
            for(int i=st[y];i<=ed[y];i++) if(a[i].se<=r) v[++c1]=a[i].fi;
            res+=merge(u,v,c0,c1);
        }
        write(res),putchar('\n');
    }
    return 0;
}

posted on 2022-06-12 19:21  peiwenjun  阅读(24)  评论(0)    收藏  举报

导航