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\)

接下来考虑如何算贡献。贡献分为两类:

  1. 对于 \((t,x,y,0)\)\(w=\sum_{(t',x',y',0/1)\in S}[t'<t][x'>x][y'<y]\)
  2. 对于 \((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]);
	}
}
posted @ 2025-03-18 20:45  XP3301_Pipi  阅读(20)  评论(0)    收藏  举报
Title