[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; }