3720: Gty的妹子树
Time Limit: 10 Sec Memory Limit: 128 MBDescription
我曾在弦歌之中听过你,
檀板声碎,半出折子戏。舞榭歌台被风吹去,岁月深处尚有余音一缕……Gty神(xian)犇(chong)从来不缺妹子……他来到了一棵妹子树下,发现每个妹子有一个美丽度……由于Gty很哲♂学,他只对美丽度大于某个值的妹子感兴趣。他想知道某个子树中美丽度大于k的妹子个数。某个妹子的美丽度可能发生变化……树上可能会出现一只新的妹子……维护一棵初始有n个节点的有根树(根节点为1),树上节点编号为1-n,每个点有一个权值wi。支持以下操作:0 u x 询问以u为根的子树中,严格大于x的值的个数。(u^=lastans,x^=lastans)1 u x 把u节点的权值改成x。(u^=lastans,x^=lastans)2 u x 添加一个编号为"当前树中节点数+1"的节点,其父节点为u,其权值为x。(u^=lastans,x^=lastans)最开始时lastans=0。Input
输入第一行包括一个正整数n(1<=n<=30000),代表树上的初始节点数。
接下来n-1行,每行2个整数u,v,为树上的一条无向边。任何时刻,树上的任何权值大于等于0,且两两不同。接下来1行,包括n个整数wi,表示初始时每个节点的权值。接下来1行,包括1个整数m(1<=m<=30000),表示操作总数。接下来m行,每行包括三个整数 op,u,v:op,u,v的含义见题目描述。保证题目涉及的所有数在int内。Output
对每个op=0,输出一行,包括一个整数,意义见题目描述。
Sample Input
2 1 2 10 20 1 0 1 5
Sample Output
2
树上分块,块与块之间是树的关系
首先将树以根号n为标准分为根号n块,怎么分呢?
对于每个节点,维护
siz[],以其为根的子树的大小
bl[],它属于哪一块
开始所有的siz[i]=1,bl[i]=i
然后在dfs过程中合并这n块,
当到i的子节点时,若父节点siz[]<根号n时,点i与他的父节点合并为一块
父节点siz[]=根号n,点i另成一块,并与父节点所在块之间连一条边
查询操作:
属于一整块的二分,不属于一块的枚举
什么意思呢?
每一块肯定都有一个根节点,即bl[i]=i的点
所以若bl[x]=x,那就二分x所在块,否则就枚举x的每个儿子
修改操作:
二分找到块内位置,直接修改,然后排序
添加操作:
如果父节点所在块大小<S,加到父节点所在块内
否则,自己另成一块,并与父节点所在块连一条边
小细节:
块与块之间连边的时候,由父节点所在块向子节点所在块连单向边,
这样在查询的时候,查询同一整块保证了从x一直往下查
或者连双向边,对于每个点记录deep[]表示点的深度
查询的时候,若下一个块的根节点的deep<当前块的根节点的deep,就跳过
#include#include #include #include #define N 30001using namespace std;int n,m,tot,S,last,ans;int front[N*2],nextt[N*4],to[N*4],Fa[N*2];int w[N*2];int siz[N*2],bl[N*2];vector g[N*2];vector block[N*2];void add(int u,int v){ to[++tot]=v;nextt[tot]=front[u];front[u]=tot; to[++tot]=u;nextt[tot]=front[v];front[v]=tot;}void dfs(int x){ for(int i=front[x];i;i=nextt[i]) { if(to[i]==Fa[x]) continue; Fa[to[i]]=x; if(siz[x] y; for(int i=front[x];i;i=nextt[i]) { if(to[i]==Fa[x]) continue; query(to[i],y); } }}void change(int x,int y){ int pos=lower_bound(block[bl[x]].begin(),block[bl[x]].end(),w[x])-block[bl[x]].begin(); block[bl[x]][pos]=y;w[x]=y; sort(block[bl[x]].begin(),block[bl[x]].end());}void insert(int u,int val){ Fa[++n]=u;add(u,n);w[n]=val; if(siz[bl[u]]