bzoj 3720: Gty的妹子树
3720: Gty的妹子树
http://www.lydsy.com/JudgeOnline/problem.php?id=3720
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
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<cstdio> #include<cmath> #include<vector> #include<algorithm> #define N 30001 using 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<int>g[N*2]; vector<int>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]<S) { siz[bl[to[i]]=bl[x]]++; block[bl[x]].push_back(w[to[i]]); } else g[bl[x]].push_back(bl[to[i]]); dfs(to[i]); } } void query(int x,int y) { if(bl[x]==x) { ans+=block[x].end()-upper_bound(block[x].begin(),block[x].end(),y); for(int i=0;i<g[bl[x]].size();i++) { int v=g[bl[x]][i]; query(v,y); } } else { ans+=w[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]]<S) { bl[n]=bl[u];block[bl[n]].push_back(val); sort(block[bl[n]].begin(),block[bl[n]].end()); } else { bl[n]=n;block[bl[n]].push_back(val); g[bl[u]].push_back(bl[n]); } } int main() { scanf("%d",&n); int x,y,z;S=sqrt(n); for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); add(x,y); } for(int i=1;i<=n;i++) scanf("%d",&w[i]),bl[i]=i,siz[i]=1; dfs(1); for(int i=1;i<=n;i++) if(bl[i]==i) block[i].push_back(w[i]),sort(block[i].begin(),block[i].end()); scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d%d%d",&z,&x,&y); x^=last;y^=last; if(!z) { query(x,y); printf("%d\n",ans); last=ans; ans=0; } else if(z==1) change(x,y); else insert(x,y); } }