[Ynoi2019 模拟赛] Yuno loves sqrt technology I

Luogu P5046

Solution (OldDriverTee)

Prob

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

\(1\leq n,m\leq 10^5\)

Time : 750 ms

Memory : 512 MiB

Solution

考虑序列分块。


归并排序可以用类似于 cdq 的思想求逆序对。

所以一个区间的逆序对问题,可以分成多个区间的逆序对问题,然后计算任意两个不同区间产生的贡献。

分别处理:

  1. 每个元素到其所在块块首这段区间的逆序对个数 \(pre_i\)

  2. 每个元素到其所在块块尾这段区间的逆序对个数 \(suf_i\)

  3. 整块间的逆序对个数 \(F_{i,j}\)

  4. \(i\) 个块中小于等于 \(j\) 的数的个数 \(cnt_{i,j}\)

对于询问 \(l,r\)

\(w_{S,T}\) 为左指针在 \(S\) 区间,右指针在 \(T\) 区间的逆序对数量。

  • \(l,r\) 在同一块中,则 \(ans=pre_r-pre_{l-1}-w_{[L_{block(l)},l),[l,r]}\)

  • 否则,\(ans=suf_l+F_{block(l)+1,block(r)-1}+pre_r+w_{[l,R_{block(l)}],[L_{block(l+1)},R_{block(r-1)}]}+w_{[L_{block(l+1)},R_{block(r-1)}],[L_{block(r)},r]}+w_{[l,R_{block(l)}],[L_{block(r)},r]}\)


不卡常。

Code

Length : 3.16 KiB

#include<bits/stdc++.h>

using namespace std;

const int N=1e5+9;
const int S=666;

int a[N],c[N],n,m;
pair<int,int> b[N];
int blk[N],L[S],R[S];
long long pre[N],suf[N],F[S][S];
int cnt[S][N];

int tr[N];
inline void Add(int x,int k){
    while(x<=n){
        tr[x]+=k;
        x+=x&-x;
    }
}
inline int Ask(int x){
    int sum=0;
    while(x){
        sum+=tr[x];
        x&=x-1;
    }
    return sum;
}
inline int Ask(int l,int r){
    if(l>r) return 0;
    return Ask(r)-Ask(l-1);
}

int A1[N],A2[N];
inline int Merge(int *a,int *b,int n,int m){
    int i=1,j=1,ans=0;
    while(i<=n&&j<=m){
        if(a[i]<b[j]) i++;
        else ans+=n-i+1,j++;
    }
    return ans;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    #define endl '\n'
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i],b[i]={a[i],i};

    int B=160;
    for(int i=1;i<=n;i++) blk[i]=(i-1)/B+1;
    for(int i=1;i<=blk[n];i++) L[i]=B*(i-1)+1,R[i]=B*i;R[blk[n]]=n;

    for(int i=1;i<=blk[n];i++){
        int lst=0;
        for(int j=L[i];j<=R[i];j++){
            Add(a[j],1);
            pre[j]=lst+=Ask(a[j]+1,n);
        }
        for(int j=L[i];j<=R[i];j++) Add(a[j],-1);
        F[i][i]=lst;
        lst=0;
        for(int j=R[i];j>=L[i];j--){
            Add(a[j],1);
            suf[j]=lst+=Ask(a[j]-1);
        }
        for(int j=R[i];j>=L[i];j--) Add(a[j],-1);
        for(int j=L[i];j<=R[i];j++) cnt[i][a[j]]++;
        for(int j=1;j<=n;j++) cnt[i][j]+=cnt[i-1][j];
        sort(b+L[i],b+R[i]+1);
        for(int j=L[i];j<=R[i];j++) c[j]=b[j].first;
    }
    for(int i=1;i<=blk[n];i++){
        for(int j=1;j<=n;j++) cnt[i][j]+=cnt[i][j-1];
    }
    for(int len=2;len<=blk[n];len++){
        for(int i=1;i<=blk[n]-len+1;i++){
            int j=i+len-1;
            F[i][j]=F[i+1][j]+F[i][j-1]-F[i+1][j-1]+Merge(c+L[i]-1,c+L[j]-1,R[i]-L[i]+1,R[j]-L[j]+1);
        }
    }

    long long lst=0;
    while(m--){
        long long l,r;
        cin>>l>>r;
        l^=lst;r^=lst;
        if(l>r) swap(l,r);
        long long ans=0,cnt1=0,cnt2=0;
        if(blk[l]==blk[r]){
            for(int i=L[blk[l]];i<=R[blk[l]];i++){
                if(l<=b[i].second&&b[i].second<=r) A2[++cnt2]=c[i];
                else if(b[i].second<l) A1[++cnt1]=c[i];
            }
            if(l!=L[blk[l]]) ans=pre[r]-pre[l-1]-Merge(A1,A2,cnt1,cnt2);
            else ans=pre[r]-Merge(A1,A2,cnt1,cnt2);
        }else{
            ans=F[blk[l]+1][blk[r]-1]+suf[l]+pre[r];
            for(int i=L[blk[l]];i<=R[blk[l]];i++){
                if(b[i].second>=l){
                    A1[++cnt1]=c[i];
                    ans+=cnt[blk[r]-1][c[i]-1]-cnt[blk[l]][c[i]-1];
                }
            }
            for(int i=L[blk[r]];i<=R[blk[r]];i++){
                if(b[i].second<=r){
                    A2[++cnt2]=c[i];
                    ans+=(cnt[blk[r]-1][n]-cnt[blk[r]-1][c[i]])-(cnt[blk[l]][n]-cnt[blk[l]][c[i]]);
                }
            }
            ans+=Merge(A1,A2,cnt1,cnt2);
        }
        cout<<ans<<endl;
        lst=ans;
    }
}
posted @ 2025-03-11 20:43  JoeyJiang  阅读(27)  评论(0)    收藏  举报