可持久化文艺平衡树

您需要写一种数据结构,来维护一个序列,其中需要提供以下操作(对于各个以往的历史版本):

  1. 在第 pp 个数后插入数 xx 。
  2. 删除第 pp 个数。
  3. 翻转区间 [l,r][l,r],例如原序列是 {5,4,3,2,1}{5,4,3,2,1},翻转区间 [2,4][2,4] 后,结果是 {5,2,3,4,1}{5,2,3,4,1}。
  4. 查询区间 [l,r][l,r] 中所有数的和。

和原本平衡树不同的一点是,每一次的任何操作都是基于某一个历史版本,同时生成一个新的版本(操作 44 即保持原版本无变化),新版本即编号为此次操作的序号。

本题强制在线。

分析

函数式Treap实现。

时空复杂度\(O(n \log n)\)

// luogu-judger-enable-o2
typedef long long ll; co int N=2e5+7;
int tot;
int root[N];
int can[N],cantop;
namespace T
{
using std::swap; int ch[N<<6][2],siz[N<<6];
int pri[N<<6],val[N<<6];
ll sum[N<<6];
bool rev[N<<6]; int newnode(int v=0)
{
int x=cantop?can[cantop--]:++tot;
ch[x][0]=ch[x][1]=0,siz[x]=1;
pri[x]=rand()<<15|rand(),val[x]=sum[x]=v;
rev[x]=0;
return x;
} int clone(int y)
{
int x=cantop?can[cantop--]:++tot;
ch[x][0]=ch[y][0],ch[x][1]=ch[y][1],siz[x]=siz[y];
pri[x]=pri[y],val[x]=val[y],sum[x]=sum[y];
rev[x]=rev[y];
return x;
} void pushup(int x)
{
siz[x]=siz[ch[x][0]]+1+siz[ch[x][1]];
sum[x]=sum[ch[x][0]]+val[x]+sum[ch[x][1]];
} void pushdown(int x)
{
if(rev[x])
{
swap(ch[x][0],ch[x][1]);
if(ch[x][0])
{
ch[x][0]=clone(ch[x][0]);
rev[ch[x][0]]^=1;
}
if(ch[x][1])
{
ch[x][1]=clone(ch[x][1]);
rev[ch[x][1]]^=1;
}
rev[x]=0;
}
} void split(int x,int k,int&l,int&r)
{
if(!x)
{
l=r=0;
return;
}
pushdown(x);
if(k<=siz[ch[x][0]])
{
r=clone(x);
split(ch[r][0],k,l,ch[r][0]);
pushup(r);
}
else
{
l=clone(x);
split(ch[l][1],k-siz[ch[x][0]]-1,ch[l][1],r);
pushup(l);
}
} int merge(int x,int y)
{
if(!x||!y)
return x+y;
if(pri[x]<pri[y])
{
pushdown(y);
ch[y][0]=merge(x,ch[y][0]);
pushup(y);
return y;
}
else
{
pushdown(x);
ch[x][1]=merge(ch[x][1],y);
pushup(x);
return x;
}
} void insert(int&rt,int k,int v)
{
int x,y;
split(rt,k,x,y);
rt=merge(x,merge(newnode(v),y));
} void erase(int&rt,int p)
{
int x,y,z;
split(rt,p,x,z);
split(x,p-1,x,y);
can[++cantop]=y;
rt=merge(x,z);
} void reverse(int&rt,int l,int r)
{
int x,y,z;
split(rt,r,x,z);
split(x,l-1,x,y);
rev[y]^=1;
rt=merge(x,merge(y,z));
} ll query(int&rt,int l,int r)
{
int x,y,z;
split(rt,r,x,z);
split(x,l-1,x,y);
ll ans=sum[y];
rt=merge(x,merge(y,z));
return ans;
}
}
using namespace T;
using namespace std; int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int n=read<int>();
ll lastans=0;
for(int i=1;i<=n;++i)
{
int v,q;
read(v),read(q);
root[i]=root[v];
if(q==1)
{
int p,x;
read(p),read(x);
p^=lastans,x^=lastans;
insert(root[i],p,x);
}
else if(q==2)
{
int p;
read(p);
p^=lastans;
erase(root[i],p);
}
else if(q==3)
{
int l,r;
read(l),read(r);
l^=lastans,r^=lastans;
reverse(root[i],l,r);
}
else
{
int l,r;
read(l),read(r);
l^=lastans,r^=lastans;
printf("%lld\n",lastans=query(root[i],l,r));
}
}
return 0;
}

可持久化平衡树

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作(对于各个以往的历史版本):

  1. 插入x数
  2. 删除x数(若有多个相同的数,因只删除一个,如果没有请忽略该操作)
  3. 查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
  4. 查询排名为x的数
  5. 求x的前驱(前驱定义为小于x,且最大的数,如不存在输出-2147483647)
  6. 求x的后继(后继定义为大于x,且最小的数,如不存在输出2147483647)

和原本平衡树不同的一点是,每一次的任何操作都是基于某一个历史版本,同时生成一个新的版本。(操作3, 4, 5, 6即保持原版本无变化)

每个版本的编号即为操作的序号(版本0即为初始状态,空树)

\(n \leq 5 \times 10^5\)

分析

可以发现非旋Treap的split和merge每次变动的都是一条链。

然后就对这条链可持久化一下就行了。

时空复杂度\(O(n \log n)\)

Hint

注意copy的部分仅限于递归处理的时候,now=0,x=0,y=0这些时候就不用可持久化了,不然会莫名其妙地错。

然后是空间问题,其实3、4、5、6操作不用可持久化,但是平衡树能A就行了,论效率平衡树肯定赶不上其他的做法。

co int MAXN=5e5*50,MAXM=5e5+7; // edit 2

int root[MAXN],tot;
struct Treap
{
int ch[MAXN][2],siz[MAXN];
int val[MAXN],pri[MAXN]; il int newnode(rg int v=0)
{
++tot;
ch[tot][0]=ch[tot][1]=0,siz[tot]=1;
val[tot]=v,pri[tot]=rand()|rand()<<16;
return tot;
} il void pushup(rg int now)
{
siz[now]=siz[ch[now][0]]+1+siz[ch[now][1]];
} il void copy(rg int x,rg int y)
{
ch[x][0]=ch[y][0],ch[x][1]=ch[y][1],siz[x]=siz[y];
val[x]=val[y],pri[x]=pri[y];
} il void split(rg int now,rg int v,rg int&x,rg int&y)
{
if(!now)
{
x=y=0;
return;
}
if(val[now]<=v)
{
x=newnode();
copy(x,now);
split(ch[x][1],v,ch[x][1],y);
pushup(x);
}
else
{
y=newnode();
copy(y,now);
split(ch[y][0],v,x,ch[y][0]);
pushup(y);
}
} il int merge(rg int x,rg int y)
{
if(!x||!y) // edit 1
return x+y;
rg int now=newnode();
if(pri[x]<pri[y])
{
copy(now,x);
ch[now][1]=merge(ch[now][1],y);
pushup(now);
}
else
{
copy(now,y);
ch[now][0]=merge(x,ch[now][0]);
pushup(now);
}
return now;
} il void ins(rg int&now,rg int v)
{
rg int x,y;
split(now,v,x,y);
now=merge(x,merge(newnode(v),y));
} il void del(rg int&now,rg int v)
{
rg int x,y,z;
split(now,v-1,x,y);
split(y,v,y,z);
y=merge(ch[y][0],ch[y][1]);
now=merge(x,merge(y,z));
} il int rank(rg int&now,rg int v)
{
rg int x,y;
split(now,v-1,x,y);
rg int ans=siz[x]+1;
now=merge(x,y);
return ans;
} il int kth(rg int now,rg int k)
{
if(!now)
return 0;
while(k)
{
if(siz[ch[now][0]]>=k)
now=ch[now][0];
else if(siz[ch[now][0]]+1==k)
return now;
else
{
k-=siz[ch[now][0]]+1;
now=ch[now][1];
}
}
return now;
} il int pre(rg int&now,rg int v)
{
rg int x,y;
split(now,v-1,x,y);
rg int ans=kth(x,siz[x]);
now=merge(x,y);
return ans;
} il int suc(rg int&now,rg int v)
{
rg int x,y;
split(now,v,x,y);
rg int ans=kth(y,1);
now=merge(x,y);
return ans;
}
}T; int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
srand(20030506);
rg int n;
read(n);
for(rg int i=1;i<=n;++i)
{
rg int v,opt,x;
read(v);read(opt);read(x);
// cerr<<"v="<<v<<" opt="<<opt<<" x="<<x<<endl;
root[i]=root[v];
if(opt==1) // insert
{
T.ins(root[i],x);
}
else if(opt==2) // delete
{
T.del(root[i],x);
}
else if(opt==3) // rank
{
printf("%d\n",T.rank(root[i],x));
}
else if(opt==4) // kth
{
printf("%d\n",T.val[T.kth(root[i],x)]);
}
else if(opt==5) // pre
{
int ans=T.pre(root[i],x);
if(ans==0)
puts("-2147483647");
else
printf("%d\n",T.val[ans]);
}
else if(opt==6) // suf
{
int ans=T.suc(root[i],x);
if(ans==0)
puts("2147483647");
else
printf("%d\n",T.val[ans]);
}
}
return 0;
}

再分析

然而对这题而言有更优的做法,主席树(可持久化权值线段树)。

将权值离散化,得到了这题较优的做法。

但是要离线,所以也是个问题。不离线的话空间会大一些,问题不大。

时间复杂度\(O(n \log n)\),常数小多了。

Hint

注意调用查询的时候,边界问题。

另外“若有多个相同的数,因只删除一个,如果没有请忽略该操作”。这个神坑点卡了我好久,非旋式Treap会自动忽略不存在的,所以就没管。

co int MAXN=5e5*20,MAXM=5e5+7;

int v[MAXN],opt[MAXN],x[MAXN];
vector<int>xlist; int root[MAXM],tot;
struct SegTree
{
int sumv[MAXN];
int L[MAXN],R[MAXN]; il void pushup(rg int now)
{
sumv[now]=sumv[L[now]]+sumv[R[now]];
// assert(sumv[now]>=0);
} il void copy(rg int x,rg int y)
{
sumv[x]=sumv[y];
L[x]=L[y],R[x]=R[y];
} il void modify(rg int&now,rg int l,rg int r,rg int p,rg int v)
{
++tot;
copy(tot,now);
now=tot;
if(l==r)
{
sumv[now]+=v;
return;
}
rg int m=(l+r)>>1;
if(p<=m)
modify(L[now],l,m,p,v);
else
modify(R[now],m+1,r,p,v);
pushup(now);
} il int sum(rg int now,rg int l,rg int r,rg int ql,rg int qr)
{
if(ql<=l&&r<=qr)
return sumv[now];
rg int m=(l+r)>>1;
if(qr<=m)
return sum(L[now],l,m,ql,qr);
if(ql>=m+1)
return sum(R[now],m+1,r,ql,qr);
return sum(L[now],l,m,ql,qr)+sum(R[now],m+1,r,ql,qr);
} il int kth(rg int now,rg int l,rg int r,rg int k)
{
if(l==r)
return l;
rg int m=(l+r)>>1;
if(sumv[L[now]]>=k)
return kth(L[now],l,m,k);
else
{
k-=sumv[L[now]];
return kth(R[now],m+1,r,k);
}
} il int pre(rg int root,rg int p)
{
rg int num=p>1?sum(root,1,xlist.size(),1,p-1):0; // edit 1:notice p=1
if(num==0) // do not exist
return 0;
else
return kth(root,1,xlist.size(),num);
} il int suc(rg int root,rg int p)
{
rg int num=p<xlist.size()?sum(root,1,xlist.size(),p+1,xlist.size()):0; // edit 2:notice p=xlist.size()
if(num==0) // do not exist
return xlist.size()+1;
else
return kth(root,1,xlist.size(),sumv[root]-num+1);
}
}T; int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
rg int n;
read(n);
for(rg int i=1;i<=n;++i)
{
read(v[i]);read(opt[i]);read(x[i]);
if(opt[i]!=4) // unless kth
xlist.push_back(x[i]);
}
sort(xlist.begin(),xlist.end());
xlist.erase(unique(xlist.begin(),xlist.end()),xlist.end());
for(rg int i=1;i<=n;++i)
{
if(opt[i]!=4)
x[i]=lower_bound(xlist.begin(),xlist.end(),x[i])-xlist.begin()+1;
// cerr<<"x "<<i<<" = "<<x[i]<<endl;
root[i]=root[v[i]];
if(opt[i]==1) // insert
{
T.modify(root[i],1,xlist.size(),x[i],1);
}
else if(opt[i]==2) // delete
{
// edit 3:if this val do not exist, you should ignore this operation
if(T.sum(root[i],1,xlist.size(),x[i],x[i])==0)
continue;
T.modify(root[i],1,xlist.size(),x[i],-1);
}
else if(opt[i]==3) // rank
{
printf("%d\n",1+(x[i]>1?T.sum(root[i],1,xlist.size(),1,x[i]-1):0)); // edit 1: notice x[i]=1
}
else if(opt[i]==4) // kth
{
// assert(1<=x[i]&&x[i]<=T.sumv[root[i]]);
printf("%d\n",xlist[T.kth(root[i],1,xlist.size(),x[i])-1]);
}
else if(opt[i]==5) // pre
{
int ans=T.pre(root[i],x[i]);
if(ans!=0)
printf("%d\n",xlist[ans-1]);
else
puts("-2147483647");
}
else if(opt[i]==6) // suc
{
int ans=T.suc(root[i],x[i]);
if(ans!=xlist.size()+1)
printf("%d\n",xlist[ans-1]);
else
puts("2147483647");
}
}
return 0;
}

三分析

其实树状数组也可以做,并且常数更小。

但是空间就必须提前开出来,并且不离线不行了。

然后不用可持久化,可以搞一个dfs。给时间点连上边,dfs的时候就修改+撤销就行了。

第一次知道这么精妙的做法,那线段树、平衡树貌似都可以这么搞,并且空间复杂度大为减小。

分享一下洛谷全站最快代码,by Mr_Spade


const int N=5e5+5;
int n,m,lgn;
int num[N],tot;
int bit[N];
inline void add(int x,int k)
{
while(x<=n)
bit[x]+=k,x+=x&-x;
return;
}
inline int ask(int x)
{
int res=0;
while(x)
res+=bit[x],x&=x-1;
return res;
}
inline int find(int x)
{
int res=0;
for(int i=lgn;~i;i--)
if((res|1<<i)<=n&&bit[res|1<<i]<x)
x-=bit[res|=1<<i];
return res+1;
}
struct oper
{
int opt,x;
}o[N];
int first[N],next[N];
int ans[N];
void dfs(int now)
{
int d=0;
register int go;
for(go=first[now];go;go=next[go])
{
switch(o[go].opt)
{
case 1:add(o[go].x,1);break;
case 2:
if(ask(o[go].x)^ask(o[go].x-1))
add(o[go].x,-1);
else
d=1;
break;
case 3:ans[go]=ask(o[go].x-1)+1;break;
case 4:ans[go]=num[find(o[go].x)];break;
case 5:
if(!(d=ask(o[go].x-1)))
ans[go]=-0x7fffffff;
else
ans[go]=num[find(d)];
break;
case 6:
if((d=ask(o[go].x))==ask(n))
ans[go]=0x7fffffff;
else
ans[go]=num[find(d+1)];
break;
}
dfs(go);
switch(o[go].opt)
{
case 1:add(o[go].x,-1);break;
case 2:
if(!d)
add(o[go].x,1);
break;
}
}
return;
}
signed main()
{
fseek(stdin,0l,2);
int len=ftell(stdin);
rewind(stdin);
fread(in,1,len,stdin);
int x;
register int i;
m=read();
for(i=1;i<=m;i++)
{
next[i]=first[x=read()];first[x]=i;
o[i].opt=read();o[i].x=read();
if(o[i].opt^4)
num[++tot]=o[i].x;
}
sort(num+1,num+tot+1);
n=unique(num+1,num+tot+1)-num-1;
for(lgn=1;1<<lgn<=n;lgn++);lgn--;
for(i=1;i<=m;i++)
if(o[i].opt^4)
o[i].x=lower_bound(num+1,num+n+1,o[i].x)-num;
dfs(0);
for(i=1;i<=m;i++)
if(o[i].opt!=1&&o[i].opt!=2)
write(ans[i]),putchar('\n');
fwrite(out,1,fout,stdout);
return 0;
}

这题的bug

正如上面看到的,这题可以乱搞。所以出题人要是想考察可持久化平衡树的话,最好还是强制在线,另外为了卡掉线段树还有01Trie,可以把节点弄成双键值,比如一个pair。

另外一个有趣的问题:持久化fhq treap在合并时不需要复制节点。正如我所说的:“这跟这题的单点插入有关,如果是单点插入那么相当于要插入的新节点无论如何都复制好了,换句话说隐式的新建了一个节点。但是如果是插入一段新节点的话就错了,因为这段的新节点没有隐式复制。”

讨论

文艺平衡树(Splay)

您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1

\(n,m \leq 1e5\)

分析

Splay Tree实现。

时间复杂度均摊\(O(\log n)\)

typedef long long ll;

co int N=1e5+7;
namespace T
{
using std::swap; int rt;
int fa[N],ch[N][2],siz[N];
bool rev[N]; void pushup(int x)
{
siz[x]=siz[ch[x][0]]+1+siz[ch[x][1]];
} void pushdown(int x)
{
if(rev[x])
{
swap(ch[x][0],ch[x][1]);
rev[ch[x][0]]^=1;
rev[ch[x][1]]^=1;
rev[x]=0;
}
} void rotate(int x,int&k)
{
int y=fa[x],z=fa[y],d=ch[y][0]==x;
if(y==k)
k=x;
else
{
if(ch[z][0]==y)
ch[z][0]=x;
else
ch[z][1]=x;
}
ch[y][d^1]=ch[x][d],fa[ch[y][d^1]]=y;
ch[x][d]=y,fa[y]=x,fa[x]=z;
pushup(x);
pushup(y);
} void splay(int x,int&k)
{
while(x!=k)
{
int y=fa[x],z=fa[y];
if(y!=k)
{
if((ch[y][0]==x)^(ch[z][0]==y))
rotate(x,k);
else
rotate(y,k);
}
rotate(x,k);
}
} void build(int l,int r,int f)
{
if(l>r)
return;
int mid=(l+r)/2;
if(mid<f)
ch[f][0]=mid;
else
ch[f][1]=mid;
fa[mid]=f,siz[mid]=1;
if(l==r)
return;
build(l,mid-1,mid);
build(mid+1,r,mid);
pushup(mid);
} int find(int x,int k)
{
pushdown(x);
int s=siz[ch[x][0]];
if(k==s+1)
return x;
if(k<=s)
return find(ch[x][0],k);
else
return find(ch[x][1],k-s-1);
} void rever(int l,int r)
{
int x=find(rt,l),y=find(rt,r+2);
splay(x,rt);
splay(y,ch[x][1]);
int z=ch[y][0];
rev[z]^=1;
}
}
using namespace T;
using namespace std; int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int n,m;
read(n),read(m);
rt=(n+3)/2;
build(1,n+2,rt);
while(m--)
{
int l,r;
read(l),read(r);
rever(l,r);
}
for(int i=2;i<=n+1;++i)
printf("%d ",find(rt,i)-1);
return 0;
}

普通平衡树

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

  1. 插入x数
  2. 删除x数(若有多个相同的数,因只删除一个)
  3. 查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
  4. 查询排名为x的数
  5. 求x的前驱(前驱定义为小于x,且最大的数)
  6. 求x的后继(后继定义为大于x,且最小的数)

\(n \leq 10^5\)

分析

用范浩强Treap实现。具体原理:





















范浩强对函数式编程在OI中的应用做了很好的引入工作。

非旋式Treap的精华在于那个merge。

merge的参数要求保证x中最大的数不大于y中最小的数。

这样在合并一个子树的时候,有两种等价情况,一种是x是y的左儿子,一种是y是x的右儿子。

选择的依据是priority,这样平衡的道理就跟普通Treap一样了。

const int INF=0x7fffffff;

const int MAXN=1e5+7;

int sz;
struct Treap
{
int val[MAXN],pri[MAXN];
int ch[MAXN][2],siz[MAXN]; void pushup(int x)
{
siz[x]=siz[ch[x][0]]+1+siz[ch[x][1]];
} int new_node(int v)
{
val[++sz]=v,
pri[sz]=rand()<<15|rand();
ch[sz][0]=ch[sz][1]=0,
siz[sz]=1;
return sz;
} int merge(int x,int y)
{
if(!x||!y)
return x+y;
if(pri[x]<pri[y])
{
ch[x][1]=merge(ch[x][1],y);
pushup(x);
return x;
}
else
{
ch[y][0]=merge(x,ch[y][0]);
pushup(y);
return y;
}
} void split(int now,int v,int&x,int&y)
{
if(!now)
x=y=0;
else
{
if(val[now]<=v)
{
x=now,split(ch[now][1],v,ch[now][1],y);
}
else
{
y=now,split(ch[now][0],v,x,ch[now][0]);
}
pushup(now);
}
} void ins(int&now,int v)
{
int x,y;
split(now,v,x,y);
now=merge(merge(x,new_node(v)),y);
} void del(int&now,int v)
{
int x,y,z;
split(now,v,x,z);
split(x,v-1,x,y);
y=merge(ch[y][0],ch[y][1]);
now=merge(merge(x,y),z);
} int rank(int&now,int v)
{
int x,y;
split(now,v-1,x,y);
int ans=siz[x]+1;
now=merge(x,y);
return ans;
} int kth(int now,int k)
{
while(1)
{
if(k<=siz[ch[now][0]])
now=ch[now][0];
else if(k==siz[ch[now][0]]+1)
return now;
else
k-=siz[ch[now][0]]+1,now=ch[now][1];
}
} int pre(int&now,int v)
{
int x,y;
split(now,v-1,x,y);
int ans=kth(x,siz[x]);
now=merge(x,y);
return ans;
} int suc(int&now,int v)
{
int x,y;
split(now,v,x,y);
int ans=kth(y,1);
now=merge(x,y);
return ans;
}
}T; int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
srand(20030506);
int root=0;
int n;
read(n);
while(n--)
{
static int opt,x;
read(opt);read(x);
if(opt==1) // insert
{
T.ins(root,x);
}
else if(opt==2) // delete
{
T.del(root,x);
}
else if(opt==3) // rank
{
printf("%d\n",T.rank(root,x));
}
else if(opt==4) // kth
{
printf("%d\n",T.val[T.kth(root,x)]);
}
else if(opt==5) // precursor
{
printf("%d\n",T.val[T.pre(root,x)]);
}
else // successor
{
printf("%d\n",T.val[T.suc(root,x)]);
}
}
// fclose(stdin);
// fclose(stdout);
return 0;
}

LG3835 【模板】可持久化平衡树的更多相关文章

  1. 【LG3835】可持久化平衡树

    [LG3835]可持久化平衡树 题面 洛谷 解法一 参考文章 rope大法好 \(rope\)基本操作: #include<ext/rope> using namespace __gnu_ ...

  2. luoguP3835 [模板]可持久化平衡树

    https://www.luogu.org/problemnew/show/P3835 因为博主精力和实力有限,学不懂 fhq treap 了,因此只介绍 leafy tree 解法 leafy tr ...

  3. 洛谷.3835.[模板]可持久化平衡树(fhq treap)

    题目链接 对每次Merge(),Split()时产生的节点都复制一份(其实和主席树一样).时间空间复杂度都为O(qlogq).(应该更大些 因为rand()?内存真的爆炸..) 对于无修改的操作实际上 ...

  4. luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树)(主席树)

    luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树) 题目 #include<iostream> #include<cstdlib> #include< ...

  5. Luogu P3835 【模板】可持久化平衡树(fhq Treap)

    P3835 [模板]可持久化平衡树 题意 题目背景 本题为题目普通平衡树的可持久化加强版. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作(对于各个以往的历史版本 ...

  6. [Luogu 3835]【模板】可持久化平衡树

    Description 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作(对于各个以往的历史版本): 插入x数 删除x数(若有多个相同的数,因只删除一个,如果没有请忽略该操作 ...

  7. 洛谷P3835 【模板】可持久化平衡树

    题目背景 本题为题目 普通平衡树 的可持久化加强版. 数据已经经过强化 感谢@Kelin 提供的一组hack数据 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作( ...

  8. P3835 【模板】可持久化平衡树

    题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作(对于各个以往的历史版本): 插入x数 删除x数(若有多个相同的数,因只删除一个,如果没有请忽略该操作) 查询x数的 ...

  9. 2021.07.02 P1383 高级打字机题解(可持久化平衡树)

    2021.07.02 P1383 高级打字机题解(可持久化平衡树) 分析: 从可以不断撤销并且查询不算撤销这一骚操作可以肯定这是要咱建一棵可持久化的树(我也只会建可持久化的树,当然,还有可持久化并查集 ...

随机推荐

  1. angular之自定义 directive

    1,指令的创建至少需要一个带有@Directive装饰器修饰的控制器类.@Directive装饰器指定了一个选择器名称,用于指出与此指令相关联的属性的名字. 2,创建一个highlight.direc ...

  2. [javascript]Dom操作笔记

    1.为一个节点同时设置多个属性 $("div[aria-describedby='F53_batch_history']").attr({"display":& ...

  3. 代码演示神器——jsfiddle

    目录: 1. 介绍 2. jsfiddle的具体使用 3. 总结 1. 介绍 很多时候,我们需要在我们写的文章或博客中,即时显示出我们写的demo,能方便的解释出我们的思路.很久之前我也写过一篇文章, ...

  4. int数组的下标为n

    int 类型的数组 先定义int n = 5: 定义int a[n] = {0}; 编译怎么不通过啊?n必须为常量如果想实现你说的,应该用宏#define n 5int a[n]={0};这样就可以了 ...

  5. DataTemplate——数据模板的一个典型例子

    下面是ListBox.ItemTemplate(数据模板)应用的“典型”例子,概述如下两点: 1:Grid部分,用来“规划” 数据 显示的 布局(即数据长成什么样子)     2:给DataTempl ...

  6. FluentData,一个轻量级开源的.NET ORM数据持久化框架

    FluentData:一种使用Fluent API的新型轻量级ORM模型  FluentData 是微型 ORM(micro-ORM)家族的一名新成员,旨在比大型 ORM(full ORM)更加易用. ...

  7. PowerDesigner用法和技巧

    PowerDesigner是一款功能非常强大的建模工具软件,足以与Rose比肩,同样是当今最著名的建模软件之一.Rose是专攻UML对象模型的建模工具,之后才向数据库建模发展,而PowerDesign ...

  8. 第十天 1-9 rhel7-文件的归档和压缩

    大纲:文件的归档和压缩1.tar命令的使用及参数解析tar.gz.bz/bz2文件的创建.查看及解压zip/unzip命令的使用 一.文件的归档和压缩 在我们的计算机中,经常会遇到有好多文件名相似或作 ...

  9. opencv 图片降噪

    —— # -*- coding: utf-8 -* import numpy as np import cv2 cap = cv2.VideoCapture(0) while True: _ , fr ...

  10. 利用PXE引导安装centos7

    # 利用PXE引导安装centos7 # ###简介### > PXE (Pre-boot Execution Environment,PXE client 在网卡的 ROM 中,当计算机引导时 ...