【bzoj4771】七彩树 树链的并+STL-set+DFS序+可持久化线段树
题目描述
输入
输出
样例输入
1
5 8
1 3 3 2 2
1 1 3 3
1 0
0 0
3 0
1 3
2 1
2 0
6 2
4 1
样例输出
1
2
3
1
1
2
1
1
题解
树链的并+STL-set+DFS序+可持久化线段树
如果没有深度限制,那么对于一种颜色,子树内包含该颜色的节点为:所有该颜色节点到根节点路径覆盖的所有节点,即树链的并。
因此对于每一种颜色求树链的并,支持链加操作;查询单个点的时候就是查询单点值。树上差分后转变为单点加、子树求和,使用DFS序转化为区间问题后使用数据结构维护。
那么有深度限制呢?我们按照深度维护可持久化线段树,第 $i$ 个版本我们只考虑深度小于等于 $i$ 的节点的影响。此时需要使用STL-set维护每个颜色的树链的并。
查询时直接查询depth[x]+d版本对应的可持久化线段树中,x节点子树内的权值和即可。
时间复杂度 $O(n\log n)$ 。
#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
using namespace std;
set<int> s[N];
set<int>::iterator it;
int c[N] , head[N] , to[N] , next[N] , cnt , fa[N][20] , deep[N] , log[N] , pos[N] , ref[N] , last[N] , tp;
int id[N] , sum[N << 6] , ls[N << 6] , rs[N << 6] , root[N] , tc;
inline void add(int x , int y)
{
to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x)
{
int i;
pos[x] = ++tp , ref[tp] = x;
for(i = 1 ; (1 << i) <= deep[x] ; i ++ ) fa[x][i] = fa[fa[x][i - 1]][i - 1];
for(i = head[x] ; i ; i = next[i]) fa[to[i]][0] = x , deep[to[i]] = deep[x] + 1 , dfs(to[i]);
last[x] = tp;
}
inline int lca(int x , int y)
{
int i;
if(deep[x] < deep[y]) swap(x , y);
for(i = log[deep[x] - deep[y]] ; ~i ; i -- )
if(deep[x] - deep[y] >= (1 << i))
x = fa[x][i];
if(x == y) return x;
for(i = log[deep[x]] ; ~i ; i -- )
if(deep[x] >= (1 << i) && fa[x][i] != fa[y][i])
x = fa[x][i] , y = fa[y][i];
return fa[x][0];
}
bool cmp(int a , int b)
{
return deep[a] < deep[b];
}
void update(int p , int a , int l , int r , int x , int &y)
{
y = ++tc , sum[y] = sum[x] + a;
if(l == r) return;
int mid = (l + r) >> 1;
if(p <= mid) rs[y] = rs[x] , update(p , a , l , mid , ls[x] , ls[y]);
else ls[y] = ls[x] , update(p , a , mid + 1 , r , rs[x] , rs[y]);
}
int query(int b , int e , int l , int r , int x)
{
if(b <= l && r <= e) return sum[x];
int mid = (l + r) >> 1 , ans = 0;
if(b <= mid) ans += query(b , e , l , mid , ls[x]);
if(e > mid) ans += query(b , e , mid + 1 , r , rs[x]);
return ans;
}
int main()
{
int T;
scanf("%d" , &T);
while(T -- )
{
cnt = tp = tc = 0;
int n , m , i , p = 1 , x , y , lastans = 0;
scanf("%d%d" , &n , &m);
for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &c[i]) , s[i].clear() , id[i] = i , head[i] = 0;
for(i = 2 ; i <= n ; i ++ ) scanf("%d" , &x) , add(x , i) , log[i] = log[i >> 1] + 1;
dfs(1) , sort(id + 1 , id + n + 1 , cmp);
for(root[0] = i = 0 ; i < n ; i ++ )
{
if(i) root[i] = root[i - 1];
while(p <= n && deep[id[p]] <= i)
{
x = y = 0;
it = s[c[id[p]]].lower_bound(pos[id[p]]);
if(it != s[c[id[p]]].end()) y = ref[*it];
if(it != s[c[id[p]]].begin()) x = ref[*--it];
update(pos[id[p]] , 1 , 1 , n , root[i] , root[i]);
if(x) update(pos[lca(x , id[p])] , -1 , 1 , n , root[i] , root[i]);
if(y) update(pos[lca(y , id[p])] , -1 , 1 , n , root[i] , root[i]);
if(x && y) update(pos[lca(x , y)] , 1 , 1 , n , root[i] , root[i]);
s[c[id[p]]].insert(pos[id[p]]) , p ++ ;
}
}
while(m -- )
{
scanf("%d%d" , &x , &y) , x ^= lastans , y = min(deep[x] + (y ^ lastans) , n - 1);
printf("%d\n" , lastans = query(pos[x] , last[x] , 1 , n , root[y]));
}
}
return 0;
}
【bzoj4771】七彩树 树链的并+STL-set+DFS序+可持久化线段树的更多相关文章
- 【BZOJ-3653】谈笑风生 DFS序 + 可持久化线段树
3653: 谈笑风生 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 628 Solved: 245[Submit][Status][Discuss] ...
- BZOJ 3653: 谈笑风生(DFS序+可持久化线段树)
首先嘛,还是太弱了,想了好久QAQ 然后,这道题么,明显就是求sigma(size[x]) (x是y的儿子且层树小于k) 然后就可以发现:把前n个节点按深度建可持久化线段树,就能用前缀和维护了 其实不 ...
- Codeforces Round #442 (Div. 2) E Danil and a Part-time Job (dfs序加上一个线段树区间修改查询)
题意: 给出一个具有N个点的树,现在给出两种操作: 1.get x,表示询问以x作为根的子树中,1的个数. 2.pow x,表示将以x作为根的子树全部翻转(0变1,1变0). 思路:dfs序加上一个线 ...
- hdu5692【dfs序】【线段树】
Snacks Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Sub ...
- 【树链剖分】【dfs序】【线段树】bzoj2836 魔法树
这道题告诉我们:树链剖分的重标号就是dfs序. #include<cstdio> #include<algorithm> using namespace std; #defin ...
- 【62测试】【状压dp】【dfs序】【线段树】
第一题: 给出一个长度不超过100只包含'B'和'R'的字符串,将其无限重复下去. 比如,BBRB则会形成 BBRBBBRBBBRB 现在给出一个区间[l,r]询问该区间内有多少个字符'B'(区间下标 ...
- HDU 5877 2016大连网络赛 Weak Pair(树状数组,线段树,动态开点,启发式合并,可持久化线段树)
Weak Pair Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others) Tota ...
- BZOJ4999:This Problem Is Too Simple!(DFS序&树上差分&线段树动态开点:区间修改单点查询)
Description 给您一颗树,每个节点有个初始值. 现在支持以下两种操作: 1. C i x(0<=x<2^31) 表示将i节点的值改为x. 2. Q i j x(0<=x&l ...
- 【DFS序】【线段树】bzoj4034 [HAOI2015]T2
分开维护树的入栈序和出栈序,用两棵线段树.回答时就是用一颗的减去另一棵的. #include<cstdio> #include<algorithm> using namespa ...
随机推荐
- HDU 2647 Reward(拓扑排序,vector实现邻接表)
Reward Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Subm ...
- MSP430F5438A的串口
设置串口,最关键的是波特率的设置,推荐一个网站,很方便地计算波特率,http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP43 ...
- 关于KMP
KMP算法,对于求b串在a串中出现的次数. 在学习KMP之前,希望大家充分掌握hash. HASH: 1.hash表:用来离散化(hash数组,hash链表) 2.Rabin-Kap算法: 可替代KM ...
- sql语句——根据身份证号提取省份、出生日期、年龄、性别。
原表 sql语句: SELECT ) ' then '北京市' ' then '天津市' ' then '河北省' ' then '山西省' ' then '内蒙古自治区' ' then '辽宁省' ...
- 异常 java.lang.IllegalArgumentException: Result Maps collection already contains value
这是因为用了一次以上(多次)mbg导致sql映射文件堆积导致的异常,删除对应的sql映射文件,然后重新生成即可. Caused by: java.lang.IllegalArgumentExcepti ...
- python中eval函数作用
eval函数就是实现list.dict.tuple与str之间的转化str函数把list,dict,tuple转为为字符串 一.字符串转换成列表 a = "[[1,2], [3,4], [5 ...
- Security6:查看授予的权限
在SQL Server的安全体系中,权限分为服务器级别(Server-Level)和数据库级别(Database-Level),用户的权限分为两种形式,分别是直接授予的权限,以及由于加入角色而获得的权 ...
- Markdown打造高逼格博客
这里首先假设读者你已经掌握了Markdown与GitHub的基本用法 如果不会, 请先自行百度或Google, 我目前还没写Markdown与GitHub的教程 看云只是一个推荐, 可以认为协助生成格 ...
- Egret(白鹭引擎)——Egret+fairyGui 实战项目入门
前言 一行白鹭上青天 需求 最近,我们老板刷刷的为了省事,给美术减压(背景有点长,不说了). 美术出 fairygui,我需要在网页上看到实时操作,并且看到效果! 需求分析 这怕是要了我的狗命啊,但是 ...
- 软件测试 —— Bug
[Bug规范] Bug标题中需包含Bug的具体位置并以[]标注 举例:[模块-子模块-页面]XXXXXXXXXXXX Bug标题尽量简明 做什么操作 + 出现什么结果,比如(点击提交按钮,出现卡顿现象 ...