题解:[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\) 节点信息合并,随后有:
其实是一个比较暴力的算法。暴力把 \(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;
}

浙公网安备 33010602011771号