[BZOJ 4771]七彩树(可持久化线段树+树上差分)
[BZOJ 4771]七彩树(可持久化线段树+树上差分)
题面
给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点。每个节点都被染上了某一种颜色,其中第i个节点的颜色为c[i]。如果c[i]=c[j],那么我们认为点i和点j拥有相同的颜色。定义depth[i]为i节点与根节点的距离。为了方便起见,你可以认为树上相邻的两个点之间的距离为1。站在这棵色彩斑斓的树前面,你将面临m个问题。
每个问题包含两个整数x和d,表示询问x子树里且depth不超过depth[x]+d的所有点中出现了多少种本质不同的颜色。请写一个程序,快速回答这些询问。
分析
先不考虑深度限制,我们考虑如何统计颜色。
我们把每种颜色开一个set,set里存储该颜色节点的dfs序。对于一对相同颜色的节点u,v,他们会对u到v的路径上的节点,和lca(u,v)到根节点路径上的节点的答案产生1的贡献。可以用树上差分算法。开一棵线段树,线段树第x个叶子节点存储节点dfs序(记作dfn) 为x的差分值,维护区间和。那么我们把dfn[u]+1,dfn[v]+1,dfn[lca(u,v)]-1即可。查询x子树的时候直接区间查询x的dfs序对应的区间。
那么我们加入一个新节点x的时候如何更新答案呢。这实际上是在处理树链的并。我们在set中找出x的前驱pre和后继nex(按照dfn排序后),dfn[x]+1,dfn[lca(pre,x)]-1,相当于把x到lca(pre,x)的路径加入答案。同理对nex进行操作。注意lca(pre,nex)往上的路径被减了两次,所以dfn[lca(pre,nex)] -1.这里画个图可以方便理解

那么有深度限制要怎么做呢?维护可持久化线段树,第i棵可持久化线段树存储深度<=i的树的答案,把节点按深度排序。依次加入对应深度的可持久化线段树。查询的时候直接在对应的线段树中查询x子树即可。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#define maxn 400000
#define maxlogn 32
using namespace std;
inline int qread(int &x){
x=0;
int sign=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') sign=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*sign;
}
inline void qprint(int x){
if(x<0){
putchar('-');
qprint(-x);
}else if(x==0){
putchar('0');
return;
}else{
if(x/10>0) qprint(x/10);
putchar('0'+x%10);
}
}
int t,n,m;
int c[maxn+5];
struct edge{
int from;
int to;
int next;
}E[maxn*2+5];
int sz=1;
int head[maxn+5];
void add_edge(int u,int v){
sz++;
E[sz].from=u;
E[sz].to=v;
E[sz].next=head[u];
head[u]=sz;
}
int log2n;
int tim;
int anc[maxn+5][maxlogn+5];
int hash_dfn[maxn+5];
int deep[maxn+5];
int lb[maxn+5],rb[maxn+5];
int id[maxn+5];
int cmp(int x,int y){
return deep[x]<deep[y];
}
void dfs(int x,int fa){
tim++;
lb[x]=tim;
deep[x]=deep[fa]+1;
anc[x][0]=fa;
hash_dfn[lb[x]]=x;
for(int i=1;i<=log2n;i++) anc[x][i]=anc[anc[x][i-1]][i-1];
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
if(y!=fa){
dfs(y,x);
}
}
rb[x]=tim;
}
int lca(int x,int y){
if(deep[x]<deep[y]) swap(x,y);
for(int i=log2n;i>=0;i--){
if(deep[anc[x][i]]>=deep[y]){
x=anc[x][i];
}
}
if(x==y) return x;
for(int i=log2n;i>=0;i--){
if(anc[x][i]!=anc[y][i]){
x=anc[x][i];
y=anc[y][i];
}
}
return anc[x][0];
}
struct per_segment_tree{
//第i棵可持久化线段树维护deep<=i,且dfn在[l,r]内的节点的不同颜色个数
struct node{
int ls;
int rs;
int cnt;
}tree[maxn*maxlogn+5];
int root[maxn+5];
int ptr;
void push_up(int x){
tree[x].cnt=tree[tree[x].ls].cnt+tree[tree[x].rs].cnt;
}
void insert(int &x,int last,int upos,int uval,int l,int r){
x=++ptr;
tree[x]=tree[last];
if(l==r){
tree[x].cnt+=uval;
return;
}
int mid=(l+r)>>1;
if(upos<=mid) insert(tree[x].ls,tree[last].ls,upos,uval,l,mid);
else insert(tree[x].rs,tree[last].rs,upos,uval,mid+1,r);
push_up(x);
}
int query(int x,int L,int R,int l,int r){
if(x==0) return tree[x].cnt;
if(L<=l&&R>=r){
return tree[x].cnt;
}
int mid=(l+r)>>1;
int ans=0;
if(L<=mid) ans+=query(tree[x].ls,L,R,l,mid);
if(R>mid) ans+=query(tree[x].rs,L,R,mid+1,r);
return ans;
}
}T;
set<int>s[maxn+5];
void ini(){
log2n=log2(n)+1;
tim=0;
sz=1;
T.ptr=0;
for(int i=1;i<=n;i++){//不要memset,会TLE
anc[i][0]=0;
c[i]=0;
deep[i]=0;
hash_dfn[i]=0;
head[i]=0;
id[i]=0;
lb[i]=0;
rb[i]=0;
s[i].clear();
T.root[i]=0;
}
}
int main(){
int u,v;
qread(t);
while(t--){
qread(n);
qread(m);
ini();
for(int i=1;i<=n;i++) qread(c[i]);
for(int i=2;i<=n;i++){
qread(u);
add_edge(u,i);
add_edge(i,u);
}
dfs(1,0);
for(int i=1;i<=n;i++) id[i]=i;
sort(id+1,id+1+n,cmp);
for(int i=1;i<=n;i++){
int x=id[i];
int pre=0,nex=0;
T.insert(T.root[deep[x]],T.root[deep[id[i-1]]],lb[x],1,1,n);
set<int>::iterator it=s[c[x]].lower_bound(lb[x]);
if(it!=s[c[x]].begin()){
set<int>::iterator it2=it;//不能直接--it,会影响下一个判断
pre=hash_dfn[*(--it2)];
T.insert(T.root[deep[x]],T.root[deep[x]],lb[lca(pre,x)],-1,1,n);
//x会对pre到x的路径上的点产生1的贡献,树上差分
//线段树里的每个店储存的都是差分值
}
if(it!=s[c[x]].end()){
nex=hash_dfn[*it];
T.insert(T.root[deep[x]],T.root[deep[x]],lb[lca(x,nex)],-1,1,n);
}
//lca(pre,nex)上方的路径被多减了一次,加回来
if(pre!=0&&nex!=0){
T.insert(T.root[deep[x]],T.root[deep[x]],lb[lca(pre,nex)],1,1,n);
}
s[c[x]].insert(lb[x]);
}
int last=0;
int x,d;
for(int i=1;i<=m;i++){
qread(x);
qread(d);
x^=last;
d^=last;
last=T.query(T.root[min(deep[x]+d,deep[id[n]])],lb[x],rb[x],1,n);
qprint(last);
putchar('\n');
}
}
}
[BZOJ 4771]七彩树(可持久化线段树+树上差分)的更多相关文章
- 主席树||可持久化线段树+离散化 || 莫队+分块 ||BZOJ 3585: mex || Luogu P4137 Rmq Problem / mex
题面:Rmq Problem / mex 题解: 先离散化,然后插一堆空白,大体就是如果(对于以a.data<b.data排序后的A)A[i-1].data+1!=A[i].data,则插一个空 ...
- BZOJ.4771.七彩树(可持久化线段树)
BZOJ 考虑没有深度限制,对整棵子树询问怎么做. 对于同种颜色中DFS序相邻的两个点\(u,v\),在\(dfn[u],dfn[v]\)处分别\(+1\),\(dfn[LCA(u,v)]\)处\(- ...
- BZOJ 4771: 七彩树 可持久化线段树+树链的并
这个思路挺有意思的 ~ 利用树链的并来保证每个颜色只贡献一次,然后用可持久化线段树维护 code: #include <set> #include <cstdio> #incl ...
- BZOJ4771七彩树——可持久化线段树+set+树链的并+LCA
给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点.每个节点都被染上了某一种颜色,其中第i个节 点的颜色为c[i].如果c[i]=c[j],那么我们认为点i和点j拥有相同的颜色.定义dept ...
- BZOJ 3483 SGU505 Prefixes and suffixes(字典树+可持久化线段树)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3483 [题目大意] 给出一些串,同时给出m对前缀后缀,询问有多少串满足给出的前缀后缀模 ...
- bzoj 2653 二分答案+可持久化线段树
首先离散化,然后我们知道如果对于一个询问的区间[l1,r1],[l2,r2],我们二分到一个答案x,将[l1,r2]区间中的元素大于等于x的设为1,其余的设为-1,那么如果[l1,r1]的最大右区间和 ...
- BZOJ 3439 Kpm的MCpassword Trie树+可持久化线段树
题目大意:给定n个字符串,对于每一个字符串求以这个字符串为后缀的字符串中第k小的编号 首先将字符串反转 那么就变成了对于每一个字符串求以这个字符串为前缀的字符串中第k小的编号 然后考虑对字符串排序 那 ...
- 归并树 划分树 可持久化线段树(主席树) 入门题 hdu 2665
如果题目给出1e5的数据范围,,以前只会用n*log(n)的方法去想 今天学了一下两三种n*n*log(n)的数据结构 他们就是大名鼎鼎的 归并树 划分树 主席树,,,, 首先来说两个问题,,区间第k ...
- 主席树[可持久化线段树](hdu 2665 Kth number、SP 10628 Count on a tree、ZOJ 2112 Dynamic Rankings、codeforces 813E Army Creation、codeforces960F:Pathwalks )
在今天三黑(恶意评分刷上去的那种)两紫的智推中,突然出现了P3834 [模板]可持久化线段树 1(主席树)就突然有了不详的预感2333 果然...然后我gg了!被大佬虐了! hdu 2665 Kth ...
随机推荐
- 详解Document.Cookie
转自:https://www.jb51.net/article/77009.htm 具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案. 同时 ...
- 2018-8-10-C#-TimeSpan-时间计算
title author date CreateTime categories C# TimeSpan 时间计算 lindexi 2018-08-10 19:16:51 +0800 2018-06-1 ...
- Sql 使用游标
DECLARE data_cursor CURSOR FOR WITH T0 AS ( SELECT COUNT(f.DeptID) SubmitCount , f.DeptID FROM biz.F ...
- Codeforces 1215E 状压DP
题意:给你一个序列,你可以交换序列中的相邻的两个元素,问最少需要交换多少次可以让这个序列变成若干个极大的颜色相同的子段. 思路:由于题目中的颜色种类很少,考虑状压DP.设dp[mask]为把mask为 ...
- $LCT$维护子树信息学习笔记
\(LCT\)维护子树信息学习笔记 昨天\(FDF\)好题分享投了 \([ZJOI2018]\)历史 这题. 然后我顺势学学这个姿势. 结果调了一年...于是写个笔记记录一下. 基本原理 比较显然地, ...
- SNOI2017 礼物
题解 设前\(n\)个人的礼物个数和为\(F_n\), 那么显然\[F_n = 2 \times F_{n-1} + i^k\] 考虑矩阵快速幂 棘手的问题是:\(i^k\)不是可以直接用矩阵乘法可以 ...
- kafka消费组、消费者
consumer group consumer instance 一个消费组可能有一个或者多个消费者.同一个消费组可以订阅一个或者多个主题.主题的某一个分区只能被消费组的某一个消费者消费.那么分区和消 ...
- 如何从word文档复制内容到富文本编辑器
在之前在工作中遇到在富文本编辑器中粘贴图片不能展示的问题,于是各种网上扒拉,终于找到解决方案,在这里感谢一下知乎中众大神以及TheViper. 通过知乎提供的思路找到粘贴的原理,通过TheViper找 ...
- FFT IP核调用与仿真之FFT数学分析
对于FFT这个IP核,我其实对它真的是又爱又恨,因为它真的耗费了我太多时间,但是随着研究的深入,遇到的问题一点点给消化解决,终于不用带着问题睡觉了,哈哈,有时候真的挺佩服自己的,遇到不懂的,不了解的, ...
- HTML基础入门学习
上一篇给大家介绍了学习HTML的准备工作,本文开始带大家步入HTML的学习 一.HTML基础 网页的组成: HTML:页面构成 css:页面样式表现 JavaScript:交互行为 HTML简介: H ...