1. 程式人生 > 其它 >P7708「Wdsr-2.7」八雲藍自動機 Ⅰ

P7708「Wdsr-2.7」八雲藍自動機 Ⅰ

有趣的莫隊題

*X. P7708「Wdsr-2.7」八雲藍自動機 Ⅰ

摘自 分治與根號資料結構學習筆記 第三部分 莫隊 例題 X.。

一道莫隊好題。私以為本題最有價值的地方在於對單點修改的轉化以及對交換兩個數的處理:需要維護原來每個位置現在的位置,以及現在每個位置原來的位置。

注意到這個單點修改並不方便實現(如果是單點加就好做了),我們可以使用一個小技巧將其轉化為交換兩個數,即新建一個 \(a_c=k\),並將其看做 \(a_x\)\(a_c\) 交換。這一步非常巧妙,因為它消滅了單點修改這一麻煩的操作。

對於多次詢問一段區間的操作結果,我們通常需要使用莫隊實現,因此考慮區間在伸縮時需要維護什麼東西。為了支援在操作序列最前面加入交換兩個數的操作

,我們不難想到維護:

  • 序列 \(a\) 在操作後變成了什麼樣。

  • \(pos_i\) 表示現位置 \(i\) 是原來的哪個位置。

  • \(rev_i\) 表示原位置 \(i\) 現在在哪。

  • \(add_i\) 表示原位置 \(i\) 上的數被查詢了多少次。

  • 當右端點右移 \(r-1\to r\) 時:

    • 若第 \(r\) 個操作是交換 \(x,y\),則交換 \(a_x,a_y\)\(pos_x,pos_y\)\(rev_{pos_x},rev_{pos_y}\)
    • 若第 \(r\) 個操作是查詢 \(x\),則令 \(ans\gets ans+a_x\)\(add_{pos_x}\gets add_{pos_x}+1\)
  • 當左端點左移 \(l+1\to l\) 時:

    • 若第 \(l\) 個操作是交換 \(x,y\),注意我們相當於交換 “原位置” 的兩個數,因此對答案有影響。令 \(del=a_{rev_y}-a_{rev_x}\),答案需加上 \(del\times (add_x-add_y)\),即計算原來的 \(a_x,a_y\) 即現在的 \(a_{rev_x},a_{rev_y}\) 在交換後的貢獻變化量。此外,交換 \(a_{rev_x},a_{rev_y}\) \(add_x,add_y\)\(rev_x,rev_y\) 以及 \(pos_{rev_x},pos_{rev_y}\)
    • 若第 \(l\)
      個操作是查詢 \(x\),則令 \(ans\gets ans+a_{rev_x}\)\(add_x\gets add_x+1\),意義顯然。

右端點左移和左端點右移的情況分別與上述兩種情況相似,不再贅述。時間複雜度 \(\mathcal{O}(n\sqrt n)\)

const int N=4e5+5;
const int B=666;
uint n,m,q,cnt,a[N],id[N],op[N],x[N],y[N];
struct query{
	int l,r,blk,id;
	bool operator < (const query &v) const {
		return blk!=v.blk?blk<v.blk:blk&1?r>v.r:r<v.r; 
	}
}c[N];

uint ans,res[N],pos[N],rev[N],add[N];
int main(){
	cin>>n>>m,cnt=n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=m;i++){
		cin>>op[i]>>x[i]; if(op[i]!=3)cin>>y[i];
		if(op[i]==1)a[++cnt]=y[i],op[i]=2,y[i]=cnt;
	}
	for(int i=1;i<=cnt;i++)pos[i]=rev[i]=i;
	cin>>q;
	for(int i=1,l,r;i<=q;i++)cin>>l>>r,c[i]={l,r,l/B,i};
	sort(c+1,c+q+1);
	for(int i=1,l=1,r=0;i<=q;i++){
		while(r<c[i].r){
			r++;
			if(op[r]==2){
				swap(pos[x[r]],pos[y[r]]),swap(a[x[r]],a[y[r]]);
				swap(rev[pos[x[r]]],rev[pos[y[r]]]);
			}
			else ans+=a[x[r]],add[pos[x[r]]]++;
		}
		while(l>c[i].l){
			l--;
			if(op[l]==2){
				uint del=a[rev[y[l]]]-a[rev[x[l]]];
				swap(rev[x[l]],rev[y[l]]),ans+=(add[x[l]]-add[y[l]])*del;
				swap(a[rev[x[l]]],a[rev[y[l]]]);
				swap(pos[rev[x[l]]],pos[rev[y[l]]]),swap(add[x[l]],add[y[l]]);
			}
			else ans+=a[rev[x[l]]],add[x[l]]++;
		}
		while(r>c[i].r){
			if(op[r]==2){
				swap(pos[x[r]],pos[y[r]]),swap(a[x[r]],a[y[r]]);
				swap(rev[pos[x[r]]],rev[pos[y[r]]]);
			}
			else ans-=a[x[r]],add[pos[x[r]]]--;
			r--;
		}
		while(l<c[i].l){
			if(op[l]==2){
				uint del=a[rev[y[l]]]-a[rev[x[l]]];
				swap(rev[x[l]],rev[y[l]]),ans+=(add[x[l]]-add[y[l]])*del;
				swap(a[rev[x[l]]],a[rev[y[l]]]);
				swap(pos[rev[x[l]]],pos[rev[y[l]]]),swap(add[x[l]],add[y[l]]);
			}
			else ans-=a[rev[x[l]]],add[x[l]]--;
			l++;
		}
		res[c[i].id]=ans;
	}
	for(int i=1;i<=q;i++)cout<<res[i]<<"\n";
	return 0;
}