CF1540D. Inverse Inversions
有某个你不知道的排列\(p\)
设\(f_i\)表示\(\sum_{j<i}[p_j>p_i]\)。给你\(f_i\)。要维护:
- 给\(f_i\)单点修改。
- 询问\(p_i\)。
\(n,Q\le 10^5\)
令\(f_i\leftarrow i-1-f_i\)。增量构造,每次将\(i\)插入到第\(f_i\)个数后面,最后得到的东西即\(p_i\)。
发现前面插入的数的具体相对顺序对后面没有影响。
于是对于每个块,预处理:模拟这样插的过程,一开始有块外前面的数,不关心其相对顺序。然后一个个插,记下插完这个块之后块内所有点的位置的序列。预处理暴力可以做到\(O(nB)\),也能用数据结构做到\(O(n\lg n)\)。
询问从\(i\)开始,先查\(i\)在这个块以及之前块的排名。然后往后枚举块,根据\(i\)之前的排名,二分出插入了后面的块后的新的排名,用这个新的排名继续做下去。时间\(O(\frac{n}{B}\lg B)\)。
修改可以用数据结构做到\(O(B\lg B)\)。不过还能更优:设小于等于\(i\)的点(包括块外前面)为黑点,将黑点从最终的排名中单独提出按照最后的顺序排好,改变\(i\)的位置(也就是将某个区间循环移位),然后再将这些黑点按照顺序放回拿出它们排列中的位置。模拟这个过程,可以做到\(O(B)\)。
于是时间就可以做到\(O(n\sqrt {n\lg n})\)啦。
说得简单实际上处理循环移位那一部分老阴间了;而且CF机子那么快写\(O(n\sqrt n\lg n)\)过去也应该是轻轻松松。
upd:杂题选讲的时候大佬们给我翻译了题解做法,然后他们将题解做法跟我的做法结合爆标了。
查\(i\)在其所在块以及之前块中排名,往后经过每个块时,算出其新的排名(算排名的增量)。这和上面是一样的。如果也维护出了每个块中的那个序列,相当于某个块内点(块内排名\(i\),整个排名\(rk_i\)),能对\(loc\ge rk_i-i+1\)的之前的排名\(loc\)产生贡献。
如果每个块用个数据结构,对\(\ge rk_i-i+1\)的打上+1的标记,询问的时候扫过去一路上单点询问就好了。
观察修改一个数有什么影响。发现除了修改点之外,发生位移的块内黑点,如果前面为块外黑点则移动一位,如果前面为白点则跳,跳的总数不会超过白点总数。于是\(rk_i-i+1\)的总变化量是\(O(B)\)的。
每个块维护个\(O(B)\)区间修改,\(O(1)\)单点修改,\(O(1)\)单点查询的数据结构。于是询问时间\(O(\frac{n}{B})\),修改时间\(O(B)\)。总时间\(O(n\sqrt n)\)。
然而并没有前面那个方法快……
如果想写题解做法,把前面单点修改那个处理循环位移的那个东西换成树状数组暴力处理。这样实现难度大大降低还不一定跑得慢。
using namespace std;
#include <bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
const int N=100005;
const int B=1500;
int n,Q;
int f[N];
int bel[N],R[N],nb;
pair<int,int> _rk[N/B+5][B+5];
int p[N];
void init(int b,pair<int,int> rk[]){
rk[R[b]-R[b-1]+1]=mp(R[b]+1,0);
for (int i=R[b-1]+1,k=1;i<=R[b];++i,++k){
int x=upper_bound(rk+1,rk+k,mp(f[i],i))-rk-1;
for (int j=k;j>x+1;--j)
rk[j]=rk[j-1],rk[j].fi++;
rk[x+1]=mp(f[i]+1,i);
}
for (int i=1;i<=R[b]-R[b-1];++i)
p[rk[i].se]=i;
}
int query(int v){
int u=_rk[bel[v]][p[v]].fi;
for (int b=bel[v]+1;b<=nb;++b){
int l=1,r=R[b]-R[b-1],res=0;
while (l<=r){
int mid=l+r>>1;
if (u>_rk[b][mid].fi-mid)
l=(res=mid)+1;
else
r=mid-1;
}
u+=res;
}
return u;
}
void mdf(int x,int c,int b,pair<int,int> rk[]){
int k=0,y=-1;
if (f[x]==c) return;
if (f[x]<c){
int k_=-1;
for (int i=1;i<=R[b]-R[b-1];++i){
if (rk[i].fi-k>c){
break;
}
if (rk[i].se>=x)
++k;
y=i,k_=k;
if (rk[i].fi-k==c){
break;
}
}
c=k_+c;
for (int i=p[x];i<y;++i)
rk[i]=rk[i+1];
rk[y]=mp(c,x);
int pos=p[x]-1;
for (int i=p[x];i<y;++i){
if (rk[i].se<=x){
if (rk[i-1].fi+1<rk[i].fi){
rk[i].fi--;
pos=i;
}
else{
for (int j=i;j>pos+1;--j)
swap(rk[j],rk[j-1]);
rk[pos+1].fi=rk[pos+2].fi-1;
pos=i;
}
}
else if (rk[i].fi+1<rk[i+1].fi)
pos=i;
}
}
else{
y=-1;
for (int i=1;i<=R[b]-R[b-1];++i){
if (rk[i].se>=x)
++k;
if (rk[i].fi-k>=c+1){
y=i;
if (rk[i].se<x)
c=k+c+1;
else
c=k-1+c+1;
break;
}
}
if (y==-1)
y=p[x],c=R[b];
for (int i=p[x];i>y;--i)
rk[i]=rk[i-1];
rk[y]=mp(c,x);
int pos=p[x]+1;
for (int i=p[x];i>y;--i){
if (rk[i].se<=x){
if (rk[i+1].fi-1>rk[i].fi){
rk[i].fi++;
pos=i;
}
else{
for (int j=i;j<pos-1;++j)
swap(rk[j],rk[j+1]);
rk[pos-1].fi=rk[pos-2].fi+1;
pos=i;
}
}
else if (rk[i].fi-1>rk[i-1].fi)
pos=i;
}
}
for (int i=1;i<=R[b]-R[b-1];++i)
p[rk[i].se]=i;
}
int main(){
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;++i){
scanf("%d",&f[i]);
f[i]=i-1-f[i];
}
for (int i=1;i<=n;++i){
bel[i]=(i-1)/B+1;
R[bel[i]]=i;
}
nb=bel[n];
for (int i=1;i<=nb;++i)
init(i,_rk[i]);
scanf("%d",&Q);
while (Q--){
int op,x,c;
scanf("%d%d",&op,&x);
if (op==1){
scanf("%d",&c);
c=x-1-c;
mdf(x,c,bel[x],_rk[bel[x]]);
f[x]=c;
}
else{
int ans=query(x);
printf("%d\n",ans);
}
}
return 0;
}
\(O(n\sqrt n)\)做法:
using namespace std;
#include <bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
const int N=100005;
const int B=1000,T=350;
int n,Q;
int f[N];
int bel[N],R[N/B+5],nb;
pair<int,int> _rk[N/B+5][B+5];
int p[N];
int Rt[N/T+5],belt[N],nbt;
int t0[N/B+5][N],t1[N/B+5][N/T+5];
#define v_mdf0(x,c) (t0[x]+=(c))
void v_mdf(int b,int l,int c){
for (int i=l;i<=Rt[belt[l]];++i)
t0[b][i]+=c;
for (int i=belt[l]+1;i<=nbt;++i)
t1[b][i]+=c;
}
#define v_qry(b,x) (t0[b][x]+t1[b][belt[x]])
void init(int b,pair<int,int> rk[]){
rk[R[b]-R[b-1]+1]=mp(R[b]+1,0);
for (int i=R[b-1]+1,k=1;i<=R[b];++i,++k){
int x=upper_bound(rk+1,rk+k,mp(f[i],i))-rk-1;
for (int j=k;j>x+1;--j)
rk[j]=rk[j-1],rk[j].fi++;
rk[x+1]=mp(f[i]+1,i);
}
for (int i=1;i<=R[b]-R[b-1];++i){
p[rk[i].se]=i;
t0[b][rk[i].fi-i+1]++;
}
for (int i=1;i<=n;++i)
t0[b][i]+=t0[b][i-1];
}
int query(int v){
int u=_rk[bel[v]][p[v]].fi;
for (int b=bel[v]+1;b<=nb;++b)
u+=v_qry(b,u);
return u;
}
void mdf(int x,int c,int b,pair<int,int> rk[],int t0[]){
int k=0,y=-1;
if (f[x]==c) return;
if (f[x]<c){
int k_=-1;
for (int i=1;i<=R[b]-R[b-1];++i){
if (rk[i].fi-k>c)
break;
if (rk[i].se>=x)
++k;
y=i,k_=k;
if (rk[i].fi-k==c)
break;
}
c=k_+c;
v_mdf(b,rk[p[x]].fi-p[x]+1,-1);
v_mdf(b,c-y+1,1);
for (int i=p[x];i<y;++i)
rk[i]=rk[i+1];
rk[y]=mp(c,x);
int pos=p[x]-1;
for (int i=p[x];i<y;++i){
v_mdf0(rk[i].fi-(i+1)+1,-1);
if (rk[i].se<=x){
if (rk[i-1].fi+1<rk[i].fi){
rk[i].fi--;
v_mdf0(rk[i].fi-i+1,1);
pos=i;
}
else{
v_mdf0(rk[i].fi-i,i-(pos+1)+1);
pair<int,int> tmp=rk[i];
for (int j=i;j>pos+1;--j)
rk[j]=rk[j-1];
rk[pos+1]=mp(rk[pos+2].fi-1,tmp.se);
pos=i;
}
}
else if (rk[i].fi+1<rk[i+1].fi)
pos=i;
}
}
else{
for (int i=1;i<=R[b]-R[b-1];++i){
if (rk[i].se>=x)
++k;
if (rk[i].fi-k>=c+1){
y=i;
if (rk[i].se<x)
c=k+c+1;
else
c=k-1+c+1;
break;
}
}
v_mdf(b,rk[p[x]].fi-p[x]+1,-1);
v_mdf(b,c-y+1,1);
for (int i=p[x];i>y;--i)
rk[i]=rk[i-1];
rk[y]=mp(c,x);
int pos=p[x]+1;
for (int i=p[x];i>y;--i){
v_mdf0(rk[i].fi-i+1,1);
if (rk[i].se<=x){
if (rk[i+1].fi-1>rk[i].fi){
rk[i].fi++;
v_mdf0((rk[i].fi-1)-i+1,-1);
pos=i;
}
else{
v_mdf0(rk[i].fi-i+1,-((pos-1)-i+1));
pair<int,int> tmp=rk[i];
for (int j=i;j<pos-1;++j)
rk[j]=rk[j+1];
rk[pos-1]=mp(rk[pos-2].fi+1,tmp.se);
pos=i;
}
}
else if (rk[i].fi-1>rk[i-1].fi)
pos=i;
}
}
for (int i=1;i<=R[b]-R[b-1];++i)
p[rk[i].se]=i;
}
int main(){
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;++i){
scanf("%d",&f[i]);
f[i]=i-1-f[i];
}
for (int i=1;i<=n;++i){
bel[i]=(i-1)/B+1;
R[bel[i]]=i;
belt[i]=(i-1)/T+1;
Rt[belt[i]]=i;
}
nb=bel[n];
nbt=belt[n];
for (int i=1;i<=nb;++i)
init(i,_rk[i]);
scanf("%d",&Q);
while (Q--){
int op,x,c;
scanf("%d%d",&op,&x);
if (op==1){
scanf("%d",&c);
c=x-1-c;
mdf(x,c,bel[x],_rk[bel[x]],t0[bel[x]]);
f[x]=c;
//init(bel[x],_rk[bel[x]]);
}
else{
int ans=query(x);
printf("%d\n",ans);
}
}
return 0;
}