简洁暴力动态树
类似于树链剖分,只不过重边不以子树大小唯一确定,而是随着询问适时改变,从而摆脱静态的限制,可以动态添边删边。
边分为实边与虚边,实边连的称为路径,用splay维护(以保证可以动态加删边),从一节点往根走时将路径上的虚边改为实边,通过splay加速上升,而两点lca为最后一个虚变实的节点。
具体实现时,每个节点要存父亲,而根节点的父亲为此链的最浅处连出去的虚边父亲,虚变实时将新的父亲先旋至它所在的树的根,再将当前节点连至新父亲的右节点(假设维护深度序,一开始我没旋wa了很久,因为双链重合),而原来的右节点就被删除了,他的父亲依旧不变只不过变为虚边父亲。
删边时,将要删节点旋至根,如果是实边则截去左子树,否则直接改父亲。
利用原来自创的双选自顶向下模板,很容易改造为自指定节点向上的,除了旋转不能一行写(感觉像写非递归treap),其他部分反而精简了。
#include <cstdio>
#include <cstdlib>
#include <cstring>
int size[300000],ak[300000],rt[300000],l[300000],r[300000];
int n,ans,m;
inline void updata(int x) {size[x]=size[l[x]]+size[r[x]]+1;}
inline int min(int x,int y) {return (x<y) ? x : y;}
inline void right(int x,int y)
{
int z=rt[y];
l[y]=r[x],rt[r[x]]=y,r[x]=y,rt[y]=x;updata(y);
if (l[z]==y) l[z]=x;else if (r[z]==y) r[z]=x;
rt[x]=z;
}
inline void left(int x,int y)
{
int z=rt[y];
r[y]=l[x],rt[l[x]]=y,l[x]=y,rt[y]=x;updata(y);
if (l[z]==y) l[z]=x;else if (r[z]==y) r[z]=x;
rt[x]=z;
}
inline void splay(int x)
{
for (int y,z;(l[rt[x]]==x)||(r[rt[x]]==x);) {
y=rt[x],z=rt[y];
if (l[y]==x) {
if (l[z]==y) right(y,z);right(x,y);
}
else {
if (r[z]==y) left(y,z);left(x,y);
}
}
updata(x);
}
inline int ask(int x)
{
int sum=0,k;
for (;;) {
splay(x);sum+=size[l[x]]+1;
if (rt[x]==n+1) break;
splay(rt[x]);
r[rt[x]]=x,x=rt[x],updata(x);
}
return sum;
}
void init()
{
int i,x,y,ch,root;
scanf("%d\n",&n);
for (i=1;i<=n;i++) scanf("%d\n",&ak[i]),rt[i]=min(n+1,i+ak[i]),size[i]=1;
scanf("%d\n",&m);
for (i=1;i<=m;i++) {
scanf("%d",&ch);
if (1==ch) {
scanf("%d\n",&x);x++;
ans=ask(x);
printf("%d\n",ans);
}
else {
scanf("%d%d\n",&x,&y);x++;
root=min(x+ak[x],n+1);
splay(x);
if (rt[x]!=root) rt[l[x]]=rt[x],l[x]=0;
ak[x]=y,rt[x]=min(x+ak[x],n+1),updata(x);
}
}
}
int main()
{
freopen("bounce.in","r",stdin);
freopen("bounce.out","w",stdout);
init();
return 0;
}