[Ynoi2019模拟赛]Yuno loves sqrt technology I

题目描述

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

题解

MD不卡了。。TMD一点都卡不动。

强制在线的话也没啥好一点的方法,只能分块预处理了。

对于每个块,我们设lef[i]表示i到这个i这个元素所在块的块头的区间逆序对,rig[i]表示到块尾的逆序对。

在设一个cnt[i][j]表示从第i个块到第j个块的逆序对。

然后考虑如何处理询问。

整块之间的可以直接O(1)取答案,对于散块,我们需要求出散块内部的答案,散块对整块的答案,散块对散块的答案。

对于散块内的,因为散块在当前块中一定是它的前缀或后缀,所以我们直接O(1)取答案。

对于散块对整块的,我们可以在维护一个tag[i][j]表示前i个块,大于/小于j的元素有多少个,然后询问就扫描散块,前缀和统计即可。

对于散块对散块的,可以在每个块内维护元素的相对大小,然后对于两个不相交的区间,可以用桶排+扫描求出逆序对。

但还有一个特殊情况,就是lr在同一个块里,前面的方法不是很实用。

我们设当前块头尾h,那么答案可以表示为ans(h-r)-ans(h-l)-ans(h~l,l~r),前两个部分已经求过了,后面的用桶排+扫描即可。

不过常数略大,开2s应该能过。

代码

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize ("Ofast")
#pragma GCC optimize ("Ofast","inline","unroll-loops")
#pragma GCC optimize ("no-stack-protector")
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cassert>
#include <cmath>
#define N 100002
#define M 340
using namespace std;
typedef long long ll;
int n,m,tr[N],mis[M],rnk[N],n1,ji[M],be[N],a[N],b[M],lef[N],tag1[M][N],tag2[M][N],rig[N],tong[N];
int q[N],pos[N];
ll ans,cnt[M][M];
inline void add(int x,int y){while(x<=n)tr[x]+=y,x+=x&-x;}
inline int query(int x){int ans=0;while(x)ans+=tr[x],x-=x&-x;return ans;}
inline int merge(int l2,int r2,int l1,int r1){
    int ans=0;
    for(int i=l1;i<=r1;++i)ji[rnk[i]]=a[i];
    q[0]=0;
    for(int i=1;i<=n1;++i)if(ji[i]){
      q[++q[0]]=ji[i];ji[i]=0;
    }
    int p=0;
    for(int i=l2;i<=r2;++i)ji[rnk[i]]=a[i];
    for(int i=1;i<=n1;++i)if(ji[i]){
        while(p<=q[0]&&(!p||q[p]<=ji[i]))p++;p--;
        ans+=p;ji[i]=0;
    }
    return ans;
}
inline int rd(){
    int x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
int main(){
    n=rd();m=rd();
    n1=320;
    for(int i=1;i<=n;++i)a[i]=rd(),be[i]=(i-1)/n1+1,pos[a[i]]=i;
    for(int i=1;i<=be[n];++i){      
        int l=(i-1)*n1+1,r=min(n,i*n1);int top=0;
        for(int j=l;j<=r;++j)b[++top]=a[j];
        sort(b+1,b+top+1);
        for(int j=1;j<=top;++j)rnk[pos[b[j]]]=j;
        for(int j=l;j<=r;++j){
            lef[j]=query(n-a[j]+1);
            if(j!=l)lef[j]+=lef[j-1];
            add(n-a[j]+1,1);tong[n-a[j]+1]=1;
        }
        int moss=0;
        for(int j=n;j>=1;--j){
          moss+=tong[n-j+1];
          tag2[i][j]=tag2[i-1][j]+moss;
        }
        for(int j=l;j<=r;++j)add(n-a[j]+1,-1),tong[n-a[j]+1]=0;
        for(int j=r;j>=l;--j){
            rig[j]=query(a[j]);
            if(j!=r)rig[j]+=rig[j+1];
            add(a[j],1);tong[a[j]]=1;
        }
        moss=0;
        for(int j=1;j<=n;++j){
          moss+=tong[j];
          tag1[i][j]=tag1[i-1][j]+moss;
        }
        for(int j=l;j<=r;++j)add(a[j],-1),tong[a[j]]=0;
    }
    for(int i=1;i<=be[n];++i){
        cnt[i][i]=lef[min(n,i*n1)];
        for(int j=i+1;j<=be[n];++j){
            cnt[i][j]=cnt[i][j-1];int x=min(n,n1*j);
            for(int k=(j-1)*n1+1;k<=x;++k){
                cnt[i][j]+=tag2[j-1][a[k]]-tag2[i-1][a[k]];
            }
            cnt[i][j]+=lef[x];
        }
    }
    int l,r;
    while(m--){
        l=rd();r=rd();l^=ans;r^=ans;
        if(l>r)l^=r^=l^=r;ans=0;
      //  if(r>n||l<1){ans=0;puts("0");continue;}
        if(be[l]==be[r]){
              ans=lef[r];if(l!=(be[l]-1)*n1+1)ans-=lef[l-1],ans-=merge((be[l]-1)*n1+1,l-1,l,r); 
        }
        else{
            int st=be[l]+1,en=be[r]-1;
           for(int i=l;i<=be[l]*n1;++i)ans=ans+tag1[en][a[i]]-tag1[st-1][a[i]];
           for(int i=en*n1+1;i<=r;++i)ans=ans+tag2[en][a[i]]-tag2[st-1][a[i]];
           ans+=cnt[st][en]+rig[l]+lef[r];
           ans+=merge(l,(st-1)*n1,en*n1+1,r);
        }
         printf("%lld\n",ans);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/ZH-comld/p/10449396.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值