题解:[ARC149D] Simultaneous Sugoroku

题目传送门

题意分析

显然与 \(x_i\) 顺序无关,不妨令其有序,容易想到平衡树维护。

将所有小于 \(0\) 的元素的集合记为 \(X\),等于 \(0\) 的元素的集合记为 \(Y\),大于 \(0\) 的元素的集合记为 \(Z\)。

单次操作即把 \(X\) 中所有元素加上 \(d\),得到 \(X'\),再把 \(Z\) 中所有元素减去 \(d\),得到 \(Z'\)。

将 \(X',Z'\) 合并再把等于 \(0\) 的元素移动至 \(Y\),剩余部分为剩余元素,把 \(Y\) 里的元素统计答案后删除即可。

分裂、合并操作显然可以 FHQ Treap 维护。加减操作打懒标记即可。

但是考虑到 \(X',Z'\) 有交,正常的 merge 不再适用。引入平衡树有交合并。

记节点 \(x\) 的左右子节点分别为 \(\operatorname{lChild}(x),\operatorname{rChild}(x)\),权值为 \(\operatorname{value}(x)\)。

合并两棵树 \(x,y\) 记为 \(\operatorname{merge}(x,y)\)。钦定 \(\operatorname{value}(x)\leq\operatorname{value}(y)\),否则交换 \(x,y\)。

将 \(y\) 按照 \(\operatorname{value}(x)\) 分别分裂为 \(l,p,r\)。(\(l\) 中元素小于 \(\operatorname{value}(x)\),\(p\) 中等于,\(r\) 中大于)

把 \(p,x\) 节点信息合并,随后有:

\[\begin{aligned} \operatorname{lChild}(x)&\leftarrow\operatorname{merge}(\operatorname{lChild}(x),l)\\ \operatorname{rChild}(x)&\leftarrow\operatorname{merge}(\operatorname{rChild}(x),r) \end{aligned} \]

其实是一个比较暴力的算法。暴力把 \(y\) 分裂,然后合并到 \(x\) 对应部分上。

但是需要注意,一定要合并 \(x,p\) 的信息,否则无法保证复杂度。因为可以卡成长度为 \(\mathcal O(n)\) 的链,就炸了。

AC 代码

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
#include<random>
using namespace std;
mt19937 Rand(time(0));
constexpr const int N=3e5;
int ans[N+1],pos[N+1];
int root;
struct FHQTreap{
	int size;
	struct node{
		int value;
		vector<int>id;
		int size,rand;
		int lChild,rChild;
		int tag;
	}t[N+1];
	
	FHQTreap(){
		size=root=0;
	}
	int create(int x,vector<int>id){
		t[++size]={x,id,1,Rand()};
		return size;
	}
	void up(int p){
		t[p].size=t[t[p].lChild].size+t[p].id.size()+t[t[p].rChild].size;
	}
	void down(int p){
		if(t[p].tag){
			t[t[p].lChild].value+=t[p].tag;
			t[t[p].lChild].tag+=t[p].tag;
			t[t[p].rChild].value+=t[p].tag;
			t[t[p].rChild].tag+=t[p].tag;
			t[0]={};
			t[p].tag=0;
		}
	}
	void split(int p,int x,int &l,int &r){
		if(!p){
			l=r=0;
			return;
		}
		down(p);
		if(t[p].value<=x){
			l=p;
			split(t[p].rChild,x,t[p].rChild,r);
		}else{
			r=p;
			split(t[p].lChild,x,l,t[r].lChild);
		}
		up(p);
	}
	int merge(int l,int r){
		if(!l||!r){
			return l|r;
		}
		if(t[l].rand<t[r].rand){
			t[l].rChild=merge(t[l].rChild,r);
			up(l);
			return l;
		}else{
			t[r].lChild=merge(l,t[r].lChild);
			up(r);
			return r;
		}
	}
	void insert(int x,int id){
		int l,r,p;
		split(root,x,l,r);
		split(l,x-1,l,p);
		if(!p){
			p=create(x,{id});
		}else{
			t[p].id.push_back(id);
		}
		root=merge(merge(l,p),r);
	}
	void add(int p,int k){
		if(!p){
			return;
		}
		t[p].value+=k;
		t[p].tag+=k;
	}
	void count(int p,int id){
		if(!p){
			return;
		}
		for(int i:t[p].id){
			ans[i]=id;
		}
		count(t[p].lChild,id);
		count(t[p].rChild,id);
	}
	void dfs(int p,vector<int>&id){
		if(!p){
			return;
		}
		for(int i:t[p].id){
			id.push_back(i);
		}
		dfs(t[p].lChild,id);
		dfs(t[p].rChild,id);
	}
	int merge0(int x,int y){
		if(!x||!y){
			return x|y;
		}
		if(t[x].value>t[y].value){
			swap(x,y);
		}
		down(x);
		down(y);
		int l,r,p;
		split(y,t[x].value,l,r);
		split(l,t[x].value-1,l,p);
		if(p){
			vector<int>id;
			dfs(p,id);
			for(int i:id){
				t[x].id.push_back(i);
			}
		}
		t[x].lChild=merge0(t[x].lChild,l);
		t[x].rChild=merge0(t[x].rChild,r);
		up(x);
		return x;
	}
	void solve(int d,int id){
		int l,r,p;
		split(root,0,l,r);
		add(l,d);
		add(r,-d);
		root=merge0(l,r);
		split(root,0,l,r);
		split(l,-1,l,root);
		count(root,id);
		root=merge(l,r);
	}
	void print(int p=root){
		if(!p){
			return;
		}
		down(p);
		for(int i:t[p].id){
			pos[i]=t[p].value;
		}
		print(t[p].lChild);
		print(t[p].rChild);
	}
}t;
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		t.insert(x,i);
	}
	for(int i=1;i<=m;i++){
		int d;
		cin>>d;
		t.solve(d,i);
	}
	t.print();
	for(int i=1;i<=n;i++){
		if(ans[i]){
			cout<<"Yes "<<ans[i]<<'\n';
		}else{
			cout<<"No "<<pos[i]<<'\n';
		}
	}
	
	cout.flush();
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}
posted @ 2026-09-25 19:31  TH911  阅读(2)  评论(0)    收藏  举报