题意
给出n个数,要求资瓷单点修改和区间逆序对,强制在线。
n,m<=50000
分析
这题好劲啊!!!
看到题就想起了bzoj 3744,只不过那一题没有单点修改。
bzoj 3744的做法是预处理ans[i,j]表示第i块到点j的逆序对数,sum[i,j]表示不大于i的数在前j块里面出现的次数。
但这题很明显不能这么干,因为维护ans和sum的复杂度太大。
那么我们考虑转换一下,设ans[i,j]表示在第i块和第j块里面分别取一个数所能产生的逆序对数,sum[i,j]表示i在前j块里面出现的次数,为了方便,我们还增加一个num[i]表示第i块内的逆序对数。
把这三个数组预处理出来的复杂度是
O(nn√logn)
,且维护起来也比较方便,只需要
O(n√logn)
的复杂度。
但我们会发现这样的话处理询问会很慢,因为是区间查询,所以我们考虑把ans的第二维用树状数组来维护,sum的第一维用树状数组来维护,那么询问的复杂度也是
O(n√logn)
,总复杂度就不超过
O(nn√logn)
了。
这题写起来细节贼多,我调了一晚上才调出来。在比赛的时候遇到这种题就算想到了估计也不会去打,毕竟码力不够啊。
代码在bzoj上跑了接近40s,幸好还不是垫底的。
代码丑求不喷。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100005
#define M 505
using namespace std;
int n,m,c[N],pos[N],end[M],sta[M],size[M],ans[M][M],sum[N][M],num[M],b[N],a[N],block;
void ins(int x,int y)
{
while (x<=n)
{
c[x]+=y;
x+=x&(-x);
}
}
int find(int x)
{
int ans=0;
while (x)
{
ans+=c[x];
x-=x&(-x);
}
return ans;
}
int query(int l,int r)
{
if (pos[l]==pos[r])
{
int tot=0;
for (int i=l;i<=r;i++)
{
tot+=find(n)-find(a[i]);
ins(a[i],1);
}
for (int i=l;i<=r;i++)
ins(a[i],-1);
return tot;
}
int tot=0;
for (int i=pos[l]+1;i<pos[r]-1;i++)
for (int j=pos[r]-1;j>i;j-=(j-i)&(-(j-i)))
tot+=ans[i][j];
for (int i=pos[l]+1;i<=pos[r]-1;i++)
tot+=num[i];
for (int i=l;i<=end[pos[l]];i++)
for (int j=a[i]-1;j;j-=j&(-j))
tot+=sum[j][pos[r]-1]-sum[j][pos[l]];
for (int i=sta[pos[r]];i<=r;i++)
{
for (int j=n;j;j-=j&(-j))
tot+=sum[j][pos[r]-1]-sum[j][pos[l]];
for (int j=a[i];j;j-=j&(-j))
tot-=sum[j][pos[r]-1]-sum[j][pos[l]];
}
for (int i=l;i<=end[pos[l]];i++)
{
tot+=find(n)-find(a[i]);
ins(a[i],1);
}
for (int i=sta[pos[r]];i<=r;i++)
tot+=find(n)-find(a[i]);
for (int i=l;i<=end[pos[l]];i++)
ins(a[i],-1);
for (int i=sta[pos[r]];i<=r;i++)
{
tot+=find(n)-find(a[i]);
ins(a[i],1);
}
for (int i=sta[pos[r]];i<=r;i++)
ins(a[i],-1);
return tot;
}
void modify(int x,int y)
{
for (int i=pos[x]+1;i<=pos[n];i++)
{
int u=lower_bound(b+sta[i],b+end[i]+1,a[x])-b,v=lower_bound(b+sta[i],b+end[i]+1,y)-b;
for (int j=i;j<=pos[n];j+=(j-pos[x])&(-(j-pos[x])))
ans[pos[x]][j]+=v-u;
}
for (int i=1;i<pos[x];i++)
{
int u=upper_bound(b+sta[i],b+end[i]+1,a[x])-b,v=upper_bound(b+sta[i],b+end[i]+1,y)-b;
for (int j=pos[x];j<=pos[n];j+=(j-i)&(-(j-i)))
ans[i][j]+=u-v;
}
for (int i=pos[x];i<=pos[n];i++)
{
for (int j=a[x];j<=n;j+=j&(-j))
sum[j][i]--;
for (int j=y;j<=n;j+=j&(-j))
sum[j][i]++;
}
a[x]=y;
num[pos[x]]=0;
for (int i=sta[pos[x]];i<=end[pos[x]];i++)
{
num[pos[x]]+=find(n)-find(a[i]);
ins(a[i],1);
}
for (int i=sta[pos[x]];i<=end[pos[x]];i++)
ins(a[i],-1);
for (int i=sta[pos[x]];i<=end[pos[x]];i++)
b[i]=a[i];
sort(b+sta[pos[x]],b+end[pos[x]]+1);
}
int main()
{
//freopen("3787.in","r",stdin);freopen("test.out","w",stdout);
scanf("%d",&n);
block=sqrt(n);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
pos[i]=(i+block-1)/block;
if (!sta[pos[i]]) sta[pos[i]]=i;
end[pos[i]]=i;
size[pos[i]]++;
}
for (int i=1;i<=pos[n];i++)
sort(b+(i-1)*block+1,b+(i-1)*block+size[i]+1);
for (int i=1;i<pos[n];i++)
{
for (int j=sta[i];j<=end[i];j++)
ins(a[j],1);
for (int j=i+1;j<=pos[n];j++)
{
int s=0;
for (int k=sta[j];k<=end[j];k++)
s+=find(n)-find(a[k]);
for (int k=j;k<=pos[n];k+=(k-i)&(-(k-i)))
ans[i][k]+=s;
}
for (int j=sta[i];j<=end[i];j++)
ins(a[j],-1);
}
for (int i=1;i<=pos[n];i++)
{
for (int j=1;j<=n;j++)
sum[j][i]=sum[j][i-1];
for (int j=sta[i];j<=end[i];j++)
sum[a[j]][i]++;
}
for (int i=1;i<=pos[n];i++)
{
for (int j=1;j<=n;j++)
sum[j][i]+=sum[j-1][i];
for (int j=n;j>=1;j--)
sum[j][i]=sum[j][i]-sum[j-(j&(-j))][i];
}
for (int i=1;i<=pos[n];i++)
{
for (int j=sta[i];j<=end[i];j++)
{
num[i]+=find(n)-find(a[j]);
ins(a[j],1);
}
for (int j=sta[i];j<=end[i];j++)
ins(a[j],-1);
}
scanf("%d",&m);
int lastans=0;
for (int i=1;i<=m;i++)
{
int op,x,y;
scanf("%d%d%d",&op,&x,&y);
x^=lastans;y^=lastans;
if (op==0)
{
lastans=query(x,y);
printf("%d\n",lastans);
}else
{
modify(x,y);
}
}
return 0;
}