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;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/16368653.html
浙公网安备 33010602011771号