【题解】P2137 Gty的妹子树
ExplodingKonjac · · 题解
【原题链接】
解题思路
其实我是跟着平衡树的标签找到这里,然后写了一发块状链表。
好像题解区里唯一一个块状链表题解和我的思路也不太一样。
首先这题需要维护子树操作,所以需要使用 dfs 序转化为序列问题,这样单点修改和子树查询都可以用(树套树?)维护。
考虑一下给节点
既然有了插入操作,你可以选择写平衡树套线段树,并且毫无疑问地爆时间爆空间。
但是我们有一个很优秀的支持插入的数据结构——块状链表。
插入操作不再赘述。对于每一个块,维护其本身代表的序列
零散块就直接在
现在只剩下最后一个问题:由于 dfn 序列是不断变化的,我们怎么知道
代码实现
#include <bits/stdc++.h>
using namespace std;
/*
省略100多行的快读快写,即后面的qin、qout
【广告】https://www.luogu.com.cn/blog/explodingkonjac/fast-io
*/
typedef long long LL;
int n,m,zt[60005],w[30005];
struct Edge{ int to,nxt; }e[60005];
int cnt,hd[30005];
inline void addEdge(int u,int v)
{ e[++cnt]=(Edge){v,hd[u]},hd[u]=cnt; }
void dfs(int u,int fa)
{
zt[++cnt]=u;
for(int i=hd[u];i;i=e[i].nxt)
if(e[i].to!=fa)
dfs(e[i].to,u);
zt[++cnt]=-u;
}
struct SplayNode
{
int siz;
SplayNode *ch[2],*fa;
SplayNode(): siz(1)
{ ch[0]=ch[1]=fa=nullptr; }
}*rt;
typedef SplayNode *pNode;
#define which(i) (i->fa->ch[1]==i)
inline void pushup(pNode i)
{
i->siz=1;
if(i->ch[0]) i->siz+=i->ch[0]->siz;
if(i->ch[1]) i->siz+=i->ch[1]->siz;
}
inline void rotate(pNode i)
{
pNode f=i->fa,gf=f->fa;
bool x=which(i);
i->fa=gf;
if(gf) gf->ch[which(f)]=i;
f->ch[x]=i->ch[!x];
if(f->ch[x]) f->ch[x]->fa=f;
i->ch[!x]=f,f->fa=i;
pushup(f),pushup(i);
}
inline void splay(pNode x,pNode y=nullptr)
{
for(pNode f;(f=x->fa)!=y;rotate(x))
if(f->fa!=y)
rotate(which(f)==which(x)?f:x);
if(!y) rt=x;
}
pNode insert(int k)
{
pNode i=rt,x;
while(true)
{
int sz=i->ch[0]?i->ch[0]->siz:0;
if(sz>=k) i=i->ch[0];
else
if(!(k-=sz+1))
{
x=new SplayNode,x->ch[1]=i->ch[1],x->fa=i;
if(i->ch[1]) i->ch[1]->fa=x;
return i->ch[1]=x,pushup(x),splay(x),x;
}
else i=i->ch[1];
}
}
int getRank(pNode i)
{ return splay(i),(i->ch[0]?i->ch[0]->siz:0)+1; }
pNode lb[60005],rb[60005];
void build1(int l,int r,pNode &i=rt,pNode f=nullptr)
{
if(l<=r)
{
i=new SplayNode,i->fa=f;
int mid=(l+r)>>1;
(zt[mid]>0?lb[zt[mid]]:rb[-zt[mid]])=i;
build1(l,mid-1,i->ch[0],i);
build1(mid+1,r,i->ch[1],i);
pushup(i);
}
}
int SIZE;
struct Block
{
int cnt,*a,*b;
Block *nxt;
Block(): cnt(0),nxt(nullptr)
{ a=new int[SIZE+5],b=new int[SIZE+5]; }
inline bool full()
{ return cnt==SIZE; }
inline void update()
{ memcpy(b+1,a+1,cnt*4),sort(b+1,b+cnt+1); }
}*head;
void build2(Block *&i,int j=1)
{
i=new Block;
for(;j<=2*n && !i->full();j++)
i->a[++i->cnt]=zt[j]>0?w[zt[j]]:-1;
i->update();
if(j<=2*n) build2(i->nxt,j);
}
Block *find(int &x)
{
Block *i=head;
for(;i->cnt<x;x-=i->cnt,i=i->nxt);
return i;
}
void insert(int p,int x)
{
Block *i=find(p),*j;
if(i->full())
{
j=i->nxt;
if(!j || j->full())
i->nxt=new Block,i->nxt->nxt=j,j=i->nxt;
for(int k=++j->cnt;k>1;k--) j->a[k]=j->a[k-1];
j->a[1]=(p==SIZE)?x:i->a[SIZE];
j->update();
}
else i->cnt++;
for(int k=i->cnt;k>p+1;k--) i->a[k]=i->a[k-1];
i->a[p+1]=x,i->update();
}
int main()
{
qin>>n,SIZE=sqrt(n);
int x,y,ans=0;
for(int i=1;i<n;i++) qin>>x>>y,addEdge(x,y),addEdge(y,x);
for(int i=1;i<=n;i++) qin>>w[i];
cnt=0,dfs(1,0),build1(1,2*n),build2(head);
qin>>m;
while(m--)
{
int opt,l,r;
Block *i,*j;
qin>>opt>>x>>y,x^=ans,y^=ans;
if(opt==0)
{
ans=0;
l=getRank(lb[x]),r=getRank(rb[x]),i=find(l),j=find(r);
if(i==j)
for(int k=l;k<=r;k++) ans+=(i->a[k]>y);
else
{
for(int k=l;k<=i->cnt;k++) ans+=(i->a[k]>y);
while((i=i->nxt)!=j)
ans+=i->cnt-(upper_bound(i->b+1,i->b+i->cnt+1,y)-i->b)+1;
for(int k=1;k<=r;k++) ans+=(j->a[k]>y);
}
qout.writeln(ans);
}
else if(opt==1)
{
l=getRank(lb[x]),i=find(l);
i->a[l]=y,i->update();
}
else
{
l=getRank(lb[x]),++n;
insert(l,-1),rb[n]=insert(l);
insert(l,y),lb[n]=insert(l);
}
}
return qout.flush(),0;
}