@xiaoziyao
2021-02-18T16:17:46.000000Z
字数 4910
阅读 1005
解题报告
P5046 [Ynoi2019 模拟赛] Yuno loves sqrt technology I解题报告:
我们已经有了低于的在线算法。——nzhtl1477
题意:给定一个长度为的排列,次询问,每次询问区间的逆序对数,强制在线。()
BF口中序列分块基础题,我并不会/fad。
首先对原序列进行分块(大小为),第个块的位置为,然后进行下列预处理:
对于第个块,我们将它进行排序(并且将每个块排序后的数组保存到数组里),然后可以把原序列与这个块归并一下(因为原序列是排列,因此我们记为的位置),计算出对于位置,第个块内有多少个数小于它,我们记为()。
然后设表示在当前块中单独贡献的逆序对数量,与分别表示当前块内表示的前缀/后缀贡献的逆序对数量,很容易预处理出来。
设表示这个位置往后一直到块内第个位置这个区间贡献的的逆序对数量,我们不难发现可以差分一下,它就等于减去到块末比小的值数量。
最后,我们还预处理两个前缀和:表示前个块,有多少个数小于(也就是对上面的求一个前缀和),表示前个块有多少个数大于第个块内所有的数(),不难发现
不难发现上述预处理的时间复杂度是。
void build(int k){
int tmps=0;
for(int i=l[k];i<=r[k];i++)
tmp[++tmps]=a[i];
sort(tmp+1,tmp+1+tmps);
for(int i=l[k];i<=r[k];i++)
b[i]=tmp[i-l[k]+1];
for(int i=1,j=0;i<=n;i++){
for(;j<tmps&&tmp[j+1]<i;j++);
atb[k][pos[i]]=j;
}
for(int i=r[k];i>=l[k];i--){
ib[i]=qry(a[i]);
ibsuf[i]=(i==r[k]? 0:ibsuf[i+1])+ib[i];
upd(a[i],1);
}
for(int i=r[k];i>=l[k];i--)
upd(a[i],-1);
for(int i=l[k];i<=r[k];i++){
ibpre[i]=(i==l[k]? 0:ibpre[i-1])+((i-l[k])-qry(a[i]));
upd(a[i],1);
}
for(int i=l[k];i<=r[k];i++)
upd(a[i],-1);
for(int i=r[k];i>=l[k];i--){
for(int j=l[k];j<=i;j++)
ibsum[j][i-l[k]+1]=ib[j]-qry(a[j]);
upd(a[i],1);
}
for(int i=r[k];i>=l[k];i--)
upd(a[i],-1);
for(int i=1;i<=n;i++)
atb[k][i]+=atb[k-1][i],btb[k][bel[i]]+=atb[k][i];
}
然后,我们考虑查询,我们设在块中,在块中。
如果,我们可以想到之前预处理的(表示这个位置往后一直到块内第个位置这个区间贡献的的逆序对数量,不难发现我们对于的求和就好了),复杂度是。
如果,那么一个个部分分析:
因此,一次查询的时间复杂度为。
long long query(int x,int y){
if(x<1||y<1||x>n||y>n||x>y)//貌似不判也行
return 0;
int p=bel[x],q=bel[y];
long long res=0;
if(p==q){
for(int i=x;i<=y;i++)
res+=ibsum[i][y-l[p]+1];
return res;
}
int tmps=0,pmts=0;
for(int i=l[p];i<=r[p];i++)
if(pos[b[i]]>=x)
tmp[++tmps]=b[i];
for(int i=l[q];i<=r[q];i++)
if(pos[b[i]]<=y)
pmt[++pmts]=b[i];
for(int i=1,j=0;i<=tmps;i++){
for(;j<pmts&&pmt[j+1]<tmp[i];j++);
res+=j;
}
for(int i=x;i<=r[p];i++)
res+=atb[q-1][i]-atb[p][i];
for(int i=p+1;i<=q-1;i++)
res+=btb[q-1][i]-btb[i][i]+ibpre[r[i]];
for(int i=l[q];i<=y;i++)
res+=(l[q]-r[p]-1)-(atb[q-1][i]-atb[p][i]);
return ibsuf[x]+res+ibpre[y];
}
时间复杂度:,因为同阶,因此设,总时间复杂度为。
多开了一个数组,结果卡了半天常数。。。
实际上如果实现精细一点,甚至不需要卡什么常(加一个快读就够了,连块长都不需要调)
记得开。
#include<stdio.h>
#include<algorithm>
#include<math.h>
using namespace std;
const int maxn=100005,maxt=345;
int n,m,t,S;
int a[maxn],b[maxn],l[maxn],r[maxn],bel[maxn],Asum[maxt],Bsum[maxn],pos[maxn],tmp[maxn],pmt[maxn];//b[l,r]=sort(a[l],a[r])
int ib[maxn]/*in block*/,atb[maxt][maxn]/*all to block*/,ibsum[maxn][maxt],ibpre[maxn],ibsuf[maxn];
long long lastans;
long long btb[maxt][maxt]/*block to block*/;
void upd(int x,int v){
for(int i=bel[x]+1;i<=t;i++)
Asum[i]+=v;
for(int i=x;i<=r[bel[x]];i++)
Bsum[i]+=v;
}
int qry(int x){
return Asum[bel[x]]+Bsum[x];
}
void build(int k){
int tmps=0;
for(int i=l[k];i<=r[k];i++)
tmp[++tmps]=a[i];
sort(tmp+1,tmp+1+tmps);
for(int i=l[k];i<=r[k];i++)
b[i]=tmp[i-l[k]+1];
for(int i=1,j=0;i<=n;i++){
for(;j<tmps&&tmp[j+1]<i;j++);
atb[k][pos[i]]=j;
}
for(int i=l[k];i<=r[k];i++){
ibpre[i]=(i==l[k]? 0:ibpre[i-1])+((i-l[k])-qry(a[i]));
upd(a[i],1);
}
for(int i=l[k];i<=r[k];i++)
upd(a[i],-1);
for(int i=r[k];i>=l[k];i--){
ib[i]=qry(a[i]);
ibsuf[i]=(i==r[k]? 0:ibsuf[i+1])+ib[i];
upd(a[i],1);
}
for(int i=r[k];i>=l[k];i--)
upd(a[i],-1);
for(int i=r[k];i>=l[k];i--){
for(int j=l[k];j<=i;j++)
ibsum[j][i-l[k]+1]=ib[j]-qry(a[j]);
upd(a[i],1);
}
for(int i=r[k];i>=l[k];i--)
upd(a[i],-1);
for(int i=1;i<=n;i++)
atb[k][i]+=atb[k-1][i],btb[k][bel[i]]+=atb[k][i];
}
void init(){
S=sqrt(n),t=n/S;
for(int i=1;i<=t;i++)
l[i]=r[i-1]+1,r[i]=i*S;
if(r[t]<n)
t++,l[t]=r[t-1]+1,r[t]=n;
for(int i=1;i<=t;i++)
for(int j=l[i];j<=r[i];j++)
bel[j]=i;
for(int i=1;i<=t;i++)
build(i);
}
long long query(int x,int y){
if(x<1||y<1||x>n||y>n||x>y)
return 0;
int p=bel[x],q=bel[y];
long long res=0;
if(p==q){
for(int i=x;i<=y;i++)
res+=ibsum[i][y-l[p]+1];
return res;
}
int tmps=0,pmts=0;
for(int i=l[p];i<=r[p];i++)
if(pos[b[i]]>=x)
tmp[++tmps]=b[i];
for(int i=l[q];i<=r[q];i++)
if(pos[b[i]]<=y)
pmt[++pmts]=b[i];
for(int i=1,j=0;i<=tmps;i++){
for(;j<pmts&&pmt[j+1]<tmp[i];j++);
res+=j;
}
for(int i=x;i<=r[p];i++)
res+=atb[q-1][i]-atb[p][i];
for(int i=p+1;i<=q-1;i++)
res+=btb[q-1][i]-btb[i][i]+ibpre[r[i]];
for(int i=l[q];i<=y;i++)
res+=(l[q]-r[p]-1)-(atb[q-1][i]-atb[p][i]);
return ibsuf[x]+res+ibpre[y];
}
inline void read(int &x){
char c=getchar();
x=0;
for(;c<'0'||c>'9';c=getchar());
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-48;
}
int main(){
read(n),read(m);
for(int i=1;i<=n;i++)
read(a[i]),pos[a[i]]=i;
init();
for(int i=1;i<=m;i++){
long long x,y;
int l,r;
read(l),read(r);
x=l,y=r;
x^=lastans,y^=lastans;
printf("%lld\n",lastans=query((int)x,(int)y));
}
return 0;
}