【WC2016】鏖战表达式
可持久化平衡树
题目大意:
交互题。
给定一个表达式,有 $k$ 种运算符,优先级按标号从小到大。
每种运算符都是二元运算符,且都满足交换律和结合律。
要支持以下操作:
- 修改一个操作数。
- 修改一个运算符。
- 区间翻转。
每次修改都是基于之前某个版本。
要求每次修改完后,输出表达式的值。
通过交互的方式强制在线。
每次你需要求 $(a\ \mathrm{op}\ b)$ 的时候,都需要调用一次 F
函数。该函数最多调用 $10^7$ 次。
题解:
基本操作是用平衡树维护运算符。
运算符是有优先级的,同级的需要在同层中维护。
而 Treap 本身就存在一个“优先级”。因此一个简单的思路是用 Treap 直接维护表达式,按照运算符为其优先级进行比较。
对于同级的,根据子树大小来随机合并。
至于从历史版本上休息,把 Treap 可持久化一下即可。
这样的树高期望可能是 $O(k+\log n)$ 的。
但是如果建树的时候是一个一个合并的话,可以给一段 $1$,一段 $2$,等等,就能把树高卡到 $O(k\log n)$。
建树的时候可以使用分治的方法,每次找当前层中最接近中点的那个就好。
修改操作数可以直接递归到底层来改,保证树高不变。
另外两个操作不怎么会分析复杂度,或许有可能使树高变为 $O(k\log n)$。
官方数据最大的点约 $4.5\times 10^6$ 次函数调用。
希望有人教我优秀的复杂度分析方法/kk
Code:
#ifdef LOCAL
#include"grader.cpp"
#endif
#include<random>
#include<vector>
#include"expr.h"
using namespace std;
const int N=40005;
random_device sd;
mt19937 mt(sd());
struct node{
int sz,op,ls,rs;
bool rev;
Data res;
}d[13000000];
int nod,rt[N],ver;
vector<int>pos[102];
inline int clone(int x){d[++nod]=d[x];return nod;}
inline void flip(int x){d[x].rev^=1,swap(d[x].ls,d[x].rs);}
inline void pushdown(int x){
if(d[x].rev){
if(d[x].ls)d[x].ls=clone(d[x].ls),flip(d[x].ls);
if(d[x].rs)d[x].rs=clone(d[x].rs),flip(d[x].rs);
d[x].rev=0;
}
}
inline void update(int x){
if(d[x].op<=100){
if(d[x].ls&&d[x].rs)d[x].res=F(d[d[x].ls].res,d[d[x].rs].res,d[x].op);
}
d[x].sz=d[d[x].ls].sz+d[d[x].rs].sz+1;
}
int merge(int x,int y){
if(!x||!y)return x|y;
pushdown(x),pushdown(y);
if(d[x].op<d[y].op||d[x].op==d[y].op&&mt()%(d[x].sz+d[y].sz)<d[x].sz){
int u=clone(x);
d[u].rs=merge(d[u].rs,y);
update(u);
return u;
}
int u=clone(y);
d[u].ls=merge(x,d[u].ls);
update(u);
return u;
}
void split(int u,int k,int&x,int&y){
if(d[u].sz==k)x=clone(u),y=0;
else if(!k)x=0,y=clone(u);
else{
pushdown(u);
if(d[d[u].ls].sz>=k)y=clone(u),split(d[y].ls,k,x,d[y].ls),update(y);
else x=clone(u),split(d[x].rs,k-1-d[d[u].ls].sz,d[x].rs,y),update(x);
}
}
inline int find(int k,int l,int r,int x){
int _r=lower_bound(pos[k].begin(),pos[k].end(),x)-pos[k].begin();
int _l=_r-1;
if(_r<(int)pos[k].size()&&_l>=0&&l<=pos[k][_l]&&pos[k][_r]<=r){
_l=pos[k][_l],_r=pos[k][_r];
if(x-_l<=_r-x)return _l;else return _r;
}else if(_r<(int)pos[k].size()&&pos[k][_r]<=r)return pos[k][_r];
else if(_l>=0&&l<=pos[k][_l])return pos[k][_l];
return 0;
}
void build(int l,int r,int k,int&o){
if(l>r)return;
if(l==r)o=l;else{
const int mid=(l+r)>>1;
int x=find(k,l,r,mid);
while(!x)x=find(++k,l,r,mid);
o=x;
build(l,x-1,k,d[o].ls),build(x+1,r,k,d[o].rs);
update(o);
}
}
void init_(int n){
for(int i=1;i<=n;++i)
pos[d[i].op].push_back(i);
build(1,n,1,rt[0]);
}
void init(int,int n,int m,int,const Data*a,const int*ops){
for(int i=0;i<n-1;++i){
d[++nod]=(node){1,101,0,0,0,a[i]};
d[++nod]=(node){1,ops[i+1],0,0,0,0};
}
d[++nod]=(node){1,101,0,0,0,a[n-1]};
init_(2*n-1);
}
void modify(int&x,int pos,Data v){
pushdown(x);
if(pos==d[d[x].ls].sz+1)d[x].res=v;
else if(pos<=d[d[x].ls].sz)
modify(d[x].ls=clone(d[x].ls),pos,v);
else modify(d[x].rs=clone(d[x].rs),pos-d[d[x].ls].sz-1,v);
update(x);
}
Data modify_data(int id,int pos,Data x){
int z=2*pos+1;
modify(rt[++ver]=clone(rt[id]),z,x);
return d[rt[ver]].res;
}
Data modify_op(int id,int pos,int new_op){
int z=2*pos;
int a,b,c;
split(rt[id],z-1,a,b);
split(b,1,b,c);
d[b].op=new_op;
rt[++ver]=merge(merge(a,b),c);
return d[rt[ver]].res;
}
Data reverse(int id,int l,int r){
l=2*l+1,r=2*r+1;
int a,b,c;
split(rt[id],r,b,c);
split(b,l-1,a,b);
flip(b);
rt[++ver]=merge(merge(a,b),c);
return d[rt[ver]].res;
}