Gty的文艺妹子序列
给定一个正整数序列a(1<=ai<=n),支持单点修改,对于每次询问,输出al...ar中的逆序对数,强制在线。
分析
由于强制在线,考虑分块。
(ans(i,j))表示在第(i)块取一个元素并在第(j)块取一个元素组成的逆序对个数。 那么每次修改操作会涉及到根号个值发生改变,询问操作时需要对第二维进行区间查询。因此第二维用树状数组维护。
(sum(i,j))表示前(i)块元素(j)的个数,那么修改操作也只会改变根号个值,询问操作需要对第二维进行区间查询,因此第二维用树状数组维护。
(num(i))表示第(i)块内逆序对个数,修改操作暴力重算。
分步讨论
设块的大小为(S).
初始化
(ans)枚举块对,左边计入树状数组,右边统计逆序对,时间复杂度(O((frac{N}{S})^2 cdot S log N)=O(frac{N^2}{S} cdot log N))
(sum)枚举块,用树状数组统计逆序对,复杂度(O(frac{N}{S} cdot N cdot log N)=O(frac{N^2}{S} cdot log N))
(num)直接树状数组,复杂度(O(frac{N}{S} cdot S cdot log N)=O(N log N))
总复杂度(O(frac{N^2}{S} cdot log N))
区间查询
-
L,R在距离小于2的块内,直接树状数组统计,复杂度(O(S cdot log N))
-
L,R在距离大于等于2的块内,中间块用ans计算,两边的用树状数组和sum计算,复杂度(O(frac{N}{S} cdot log frac{N}{S}+S cdot log N))
总复杂度(O(frac{N}{S} cdot log frac{N}{S}+S cdot log N))
单点修改
(ans)利用(sum)修改,复杂度(O(frac{N}{S} cdot log N))
(sum)直接修改,复杂度(O(frac{N}{S} cdot log N))
(num)直接修改,复杂度(O(S cdot log N))
总复杂度(O(frac{N}{S} cdot log N+S cdot log N))
总复杂度
由于(M,N)同阶
当两项相等时,取等号,而此时
const int MAXN=5e4+7,MAXB=250;
int n,m,blo;
int bl[MAXN],a[MAXN];
int tree[MAXN];
int ans[MAXB][MAXB],sum[MAXB][MAXN],num[MAXB];
int lowbit(int x)
{
return x&-x;
}
void change(int p,int v) // tree
{
while(p<=n)
{
tree[p]+=v;
p+=lowbit(p);
}
}
int query(int p)
{
int res=0;
while(p)
{
res+=tree[p];
p-=lowbit(p);
}
return res;
}
void change2(int id,int p,int v) // ans
{
while(p<=bl[n])
{
ans[id][p]+=v;
p+=lowbit(p);
}
}
int query2(int id,int p)
{
int res=0;
while(p)
{
res+=ans[id][p];
p-=lowbit(p);
}
return res;
}
void change3(int id,int p,int v) // sum
{
while(p<=n)
{
sum[id][p]+=v;
p+=lowbit(p);
}
}
int query3(int id,int p)
{
int res=0;
while(p)
{
res+=sum[id][p];
p-=lowbit(p);
}
return res;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(n);
blo=sqrt(n);
for(int i=1;i<=n;++i)
{
read(a[i]);
bl[i]=(i-1)/blo+1; // belong
}
for(int i=1;i<bl[n];++i)
{
for(int j=(i-1)*blo+1;j<=i*blo;++j)
change(a[j],1);
for(int j=i+1;j<=bl[n];++j) // cal ans(i,j)
{
int t=0;
for(int k=(j-1)*blo+1;k<=min(j*blo,n);++k)
t+=query(n)-query(a[k]);
change2(i,j,t);
}
for(int j=(i-1)*blo+1;j<=i*blo;++j)
change(a[j],-1);
}
for(int i=1;i<=bl[n];++i) // cal sum(i,)
for(int j=1;j<=min(i*blo,n);++j)
change3(i,a[j],1);
for(int i=1;i<=bl[n];++i) // cal num(i)
{
for(int j=(i-1)*blo+1;j<=min(i*blo,n);++j)
{
num[i]+=query(n)-query(a[j]);
change(a[j],1);
}
for(int j=(i-1)*blo+1;j<=min(i*blo,n);++j)
change(a[j],-1);
}
read(m);
int now=0;
while(m--)
{
int opt;
read(opt);
if(opt==0)
{
int L,R;
read(L);read(R);
L^=now,R^=now;
now=0;
int l=bl[L],r=bl[R];
if(r-l<=1) // in no more than 2 blocks
{
for(int i=L;i<=R;++i)
{
now+=query(n)-query(a[i]);
change(a[i],1);
}
for(int i=L;i<=R;++i)
change(a[i],-1);
}
else
{
for(int i=l+1;i<r;++i) // middle
{
now+=num[i];
now+=query2(i,r-1)-query2(i,i);
}
for(int i=L;i<=l*blo;++i) // left
{
now+=query(n)-query(a[i]);
now+=query3(r-1,a[i]-1)-query3(l,a[i]-1);
change(a[i],1);
}
for(int i=(r-1)*blo+1;i<=R;++i) // right
{
now+=query(n)-query(a[i]);
now+=query3(r-1,n)-query3(l,n)-query3(r-1,a[i])+query3(l,a[i]);
change(a[i],1);
}
for(int i=L;i<=l*blo;++i)
change(a[i],-1);
for(int i=(r-1)*blo+1;i<=R;++i)
change(a[i],-1);
}
printf("%d
",now);
}
else if(opt==1)
{
int p,v;
read(p);read(v);
p^=now,v^=now;
for(int i=bl[p]+1;i<=bl[n];++i) // modify right ans
{
int t=query3(i,v-1)-query3(i-1,v-1); // add new
t-=query3(i,a[p]-1)-query3(i-1,a[p]-1); // del old
change2(bl[p],i,t);
}
for(int i=1;i<bl[p];++i) // modify left ans
{
int t=query3(i,n)-query3(i-1,n)-query3(i,v)+query3(i-1,v); // add new
t-=query3(i,n)-query3(i-1,n)-query3(i,a[p])+query3(i-1,a[p]); // del old
change2(i,bl[p],t);
}
for(int i=bl[p];i<=bl[n];++i) //modify suffix sum
{
change3(i,a[p],-1);
change3(i,v,1);
}
a[p]=v;
num[bl[p]]=0;
for(int i=(bl[p]-1)*blo+1;i<=min(bl[p]*blo,n);++i) // cal num
{
num[bl[p]]+=query(n)-query(a[i]);
change(a[i],1);
}
for(int i=(bl[p]-1)*blo+1;i<=min(bl[p]*blo,n);++i)
change(a[i],-1);
}
}
// fclose(stdin);
// fclose(stdout);
return 0;
}