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

Link

题意

给你一个长度为 n 的排列 aq 次询问区间逆序对个数,强制在线

时间 750ms1n,q,ai105.

思路

其实是算有贡献的二元组 (i,j)(ai>aj,lijr) 的个数。

排列其实没有什么性质,只是避免了相等情况的讨论。

于是显然会想到分类讨论:

设左散块、整块、右散块分别为 a,b,c,询问左端为块 bL,右端为块 bRa 的右端点为 Lc 的左端点为 R

  1. ia,ja:预处理每个位置到块末的答案 si 即可。答案为 sl
  2. ic,jc:预处理每个位置到块首的答案 pi 即可。答案为 pr
  3. ib,jb:预处理每两个块间的答案 gi,j 即可。答案为 gbL+1,bR1
  4. ia,jb:预处理前 i 块中小于 j 的数 fi,j=k=1it=blkbrk[atj] 即可。答案为 iafbR1,aifbL,ai
  5. ib,jc:用上一类的东西即可。答案为 ic(RL+1)fbR1,ai+fbL,ai
  6. ia,jc:预处理块内排序。将两散块排序后的数组归并比计算即可。

但我们忽略了一点:当 l,r 同块时没有处理。但我们预处理出 hi,ji,j 同块)并适当压缩空间即可。

这样做到了总时空 O(nn)


但是,由于常数较大,我不经任何常数处理的代码只能得到 40 分。

于是我们需要优化:

  1. 调块长:喜闻乐见。根据常数调到 150170
  2. 预处理削常数:psh 直接得到,f 用二维前缀和,减少树状数组或者值域分块的使用。
  3. 询问削常数:如果 Rr1<rblbR 那么我们就用到 R 的答案容斥掉多算的部分。左散块同理。

居然就能过了,这比第四分块的卡常简单些。


代码好写,以下代码仅供参考:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff

}
const int N=1e5+10,sq=170,nq=N/sq+10;
int n,q,blk,a[N],pre[N],bl[nq],br[nq],bel[N];
int pr[N],sf[N],f[nq][N],h[N][sq+10],se[nq][nq];
ll g[nq][nq],last;
struct Tem{
    int x,id;
    inline friend bool operator<(const Tem &a,const Tem &b){
        return a.x<b.x;
    }
}b[N];
inline void init(int id,int l,int r){
    int p=l-1;
    for(int i=l;i<=r;i++)
        f[id][a[i]]++;
    for(int i=l;i<=r;i++)
        b[i].x=a[i],b[i].id=i;
    sort(b+l,b+r+1);
    for(int j=l;j<=r;j++)
        for(int i=j-1;i>=l;i--)
            h[i][j-l]=h[i+1][j-l]+h[i][j-1-l]-h[i+1][j-1-l]+(a[i]>a[j]);
    for(int i=l+1;i<=r;i++)
        pr[i]=h[l][i-l];
    for(int i=r-1;i>=l;i--)
        sf[i]=h[i][r-l];
}
int s1[N],s2[N];
int main(){
    n=read(),q=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    for(int i=1;i<=n;i+=sq){
        blk++;
        bl[blk]=i,br[blk]=min(i+sq-1,n);
        for(int j=bl[blk];j<=br[blk];j++)
            bel[j]=blk;
        init(blk,bl[blk],br[blk]);
        g[blk][blk]=pr[br[blk]];
    }
    for(int i=1;i<=blk;i++)
        for(int j=1;j<=n;j++)
            f[i][j]+=f[i-1][j]+f[i][j-1]-f[i-1][j-1];
    for(int i=1;i<=blk;i++)
        for(int j=i;j<=blk;j++){
            int tmp=0;
            for(int k=bl[j];k<=br[j];k++)
                tmp+=f[i][a[k]];
            se[i][j]=tmp;
        }
    for(int j=1;j<=blk;j++)
        for(int i=j-1;i>=1;i--){
            ll cnt=g[i][j-1]+g[i+1][j]-g[i+1][j-1];
            for(int k=bl[i];k<=br[i];k++)
                cnt+=f[j][a[k]]-f[j-1][a[k]];
            g[i][j]=cnt;
        }
    while(q--){
        int l=read()^last,r=read()^last;
        int bL=bel[l],bR=bel[r];
        if(bL==bR){
            last=h[l][r-bl[bL]];
        }else{
            last=sf[l]+pr[r]+g[bL+1][bR-1];
            int len=br[bR-1]-bl[bL+1]+1;
            for(int i=l;i<=br[bL];i++)
                last+=f[bR-1][a[i]]-f[bL][a[i]];
            int L=bl[bR],R=br[bR];
            if(R-r-1<r-L){
                last+=(r-L+1ll)*len-(se[bR-1][bR]-se[bL][bR]);
                for(int i=r+1;i<=R;i++)
                    last+=f[bR-1][a[i]]-f[bL][a[i]];
            }else{
                for(int i=L;i<=r;i++)
                    last+=len-f[bR-1][a[i]]+f[bL][a[i]];
            }
            int top1=0,top2=0;
            for(int i=bl[bL];i<=br[bL];i++)
                if(b[i].id>=l) s1[++top1]=b[i].x;
            for(int i=bl[bR];i<=br[bR];i++)
                if(b[i].id<=r) s2[++top2]=b[i].x;
            int i=0,j=0,c=0;
            for(;i<top1&&j<top2;)
                if(s1[i+1]<s2[j+1]) last+=c,i++;
                else c++,j++;
            last+=1ll*c*(top1-i);
        }
        write(last),putc('\n');
    }
    flush();
}

再见 qwq~

posted @   ffffyc  阅读(5)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示