P9068 [Ynoi Easy Round 2022] 超人机械 TEST_95 做题记录
P9068 [Ynoi Easy Round 2022] 超人机械 TEST_95
Description
给定一个序列 \(a\) ,我们定义一个二元组 \((i,j)\) 为一个逆序对当且仅当 \(i<j\) 且 \(a_i>a_j\) 。定义两个逆序对 \((i_1,j_1),(i_2,j_2)\) 本质不同 当且仅当 \(a_{i_1}\ne a_{i_2}\) 或 \(a_{j_1}\ne a_{j_2}\) 。
现在给出 \(a\) 序列,问本质不同逆序对个数。
有 \(q\) 组修改,修改 \(a_x\) 为 \(y\),询问之间不独立。对于每一次修改输出序列本质不同逆序对个数。
\(1\le n,q \le 10^5, 1\le a_i, x, y, \le n\) 。
Solution
首先考虑没有修改怎么做。
我们设 \(fst_x\) 为 \(x\) 这个数字第一次出现的位置,\(lst_x\) 为 \(x\) 这个数字最后一次出现的位置。
对于一个逆序对 \(((x,a_x),(y,a_y))\),如果 \(x=fst_{a_x},y=lst_{a_y}\),我们才把它计入答案。首先这样一定会计入全部合法的逆序对,其次每一种本质相同的逆序对只会被计入一次。
树状数组直接维护即可。
接下来考虑有了修改怎么做。
注意到每一次修改 \(a_x\) 的值,只会改变 \(O(1)\) 个 \(fst\) 与 \(lst\)。
那么我们设 \((t,x,y,0)\) 表示在 \(t\) 时刻,$ x$ 变为了 \(fst_y\);\((t,x,y,1)\) 表示在 \(t\) 时刻,$ x$ 变为了 \(lst_y\)。
我们先按照操作顺序,给每一个四元组都钦定一个唯一的 \(t\)。
接下来考虑如何算贡献。贡献分为两类:
- 对于 \((t,x,y,0)\),\(w=\sum_{(t',x',y',0/1)\in S}[t'<t][x'>x][y'<y]\);
- 对于 \((t,x,y,1)\),\(w=\sum_{(t',x',y',0/1)\in S}[t'<t][x'<x][y'>y]\)。
由于每一个四元组 \(t\) 都是唯一的,所以一个逆序对只会在两种贡献中出现一次。
set 维护 \(fst,lst\),两次 CDQ 分治分别处理两类贡献。
实现中可以不记录 \(t\),因为我们的四元组本身就是按照操作顺序加入的。
时间复杂度 \(O(n\log^2 n)\),空间复杂度线性。
int n,a[N],ml,mr,Q,p[M],q[M];
struct Node{
int x,y,v,id;
}cl[M],cr[M];
set<int> s[N];
ll ans[N];
struct FenWick{
int tr[N];
void Update(int x,int v){
for(;x<=n;x+=x&-x) tr[x]+=v;
}
int Ask(int x){
int res=0;
for(;x;x-=x&-x) res+=tr[x];
return res;
}
int Ask(int l,int r){
return Ask(r)-Ask(l-1);
}
}Bit;
void SolveCDQ1(int l,int r){
if(l==r) return;
int mid=(l+r)>>1;
SolveCDQ1(l,mid); SolveCDQ1(mid+1,r);
int i=l,j=mid+1,k=l;
while(i<=mid&&j<=r){
if(cl[p[i]].x>cr[p[j]].x){
if(cl[p[i]].id==-1)
Bit.Update(cl[p[i]].y,cl[p[i]].v);
q[k++]=p[i++];
}
else{
if(cl[p[j]].id!=-1){
int res=cl[p[j]].v*Bit.Ask(cl[p[j]].y-1);
ans[cl[p[j]].id]+=res;
}
q[k++]=p[j++];
}
}
while(i<=mid){
if(cl[p[i]].id==-1)
Bit.Update(cl[p[i]].y,cl[p[i]].v);
q[k++]=p[i++];
}
while(j<=r){
if(cl[p[j]].id!=-1){
int res=cl[p[j]].v*Bit.Ask(cl[p[j]].y-1);
ans[cl[p[j]].id]+=res;
}
q[k++]=p[j++];
}
for(i=l;i<=mid;i++)
if(cl[p[i]].id==-1)
Bit.Update(cl[p[i]].y,-cl[p[i]].v);
for(i=l;i<=r;i++) p[i]=q[i];
}
void SolveCDQ2(int l,int r){
if(l==r) return;
int mid=(l+r)>>1;
SolveCDQ2(l,mid); SolveCDQ2(mid+1,r);
int i=l,j=mid+1,k=l;
while(i<=mid&&j<=r){
if(cr[p[i]].x<cr[p[j]].x){
if(cr[p[i]].id==-1)
Bit.Update(cr[p[i]].y,cr[p[i]].v);
q[k++]=p[i++];
}
else{
if(cr[p[j]].id!=-1){
int res=cr[p[j]].v*Bit.Ask(cr[p[j]].y+1,n);
ans[cr[p[j]].id]+=res;
}
q[k++]=p[j++];
}
}
while(i<=mid){
if(cr[p[i]].id==-1)
Bit.Update(cr[p[i]].y,cr[p[i]].v);
q[k++]=p[i++];
}
while(j<=r){
if(cr[p[j]].id!=-1){
int res=cr[p[j]].v*Bit.Ask(cr[p[j]].y+1,n);
ans[cr[p[j]].id]+=res;
}
q[k++]=p[j++];
}
for(i=l;i<=mid;i++){
if(cr[p[i]].id==-1)
Bit.Update(cr[p[i]].y,-cr[p[i]].v);
}
for(i=l;i<=r;i++) p[i]=q[i];
}
signed main(){
read(n);
for(int i=1;i<=n;i++){
read(a[i]);
s[a[i]].insert(i);
}
for(int i=1;i<=n;i++){
if(s[i].empty()) continue;
cl[++ml]={*s[i].begin(),i,1,0};
cr[++mr]={*s[i].begin(),i,1,-1};
cl[++ml]={*s[i].rbegin(),i,1,-1};
cr[++mr]={*s[i].rbegin(),i,1,0};
}
read(Q);
for(int i=1;i<=Q;i++){
int x,y; read(x),read(y);
if(a[x]==y) continue;
auto t=s[a[x]].begin();
if(*t==x){
cl[++ml]={x,a[x],-1,i};
cr[++mr]={x,a[x],-1,-1};
if(next(t)!=s[a[x]].end()){
cl[++ml]={*next(t),a[x],1,i};
cr[++mr]={*next(t),a[x],1,-1};
}
}
t=prev(s[a[x]].end());
if(*t==x){
cl[++ml]={x,a[x],-1,-1};
cr[++mr]={x,a[x],-1,i};
if(t!=s[a[x]].begin()){
cl[++ml]={*prev(t),a[x],1,-1};
cr[++mr]={*prev(t),a[x],1,i};
}
}
s[a[x]].erase(x);
a[x]=y;
s[a[x]].insert(x);
t=s[a[x]].begin();
if(*t==x){
cl[++ml]={x,a[x],1,i};
cr[++mr]={x,a[x],1,-1};
if(next(t)!=s[a[x]].end()){
cl[++ml]={*next(t),a[x],-1,i};
cr[++mr]={*next(t),a[x],-1,-1};
}
}
t=prev(s[a[x]].end());
if(*t==x){
cl[++ml]={x,a[x],1,-1};
cr[++mr]={x,a[x],1,i};
if(t!=s[a[x]].begin()){
cl[++ml]={*prev(t),a[x],-1,-1};
cr[++mr]={*prev(t),a[x],-1,i};
}
}
}
for(int i=1;i<=ml;i++) p[i]=i;
SolveCDQ1(1,ml);
for(int i=1;i<=mr;i++) p[i]=i;
SolveCDQ2(1,mr);
for(int i=0;i<=Q;i++){
if(i) ans[i]+=ans[i-1];
printf("%lld\n",ans[i]);
}
}

浙公网安备 33010602011771号