[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]七彩树(可持久化线段树+树上差分)的更多相关文章

  1. 主席树||可持久化线段树+离散化 || 莫队+分块 ||BZOJ 3585: mex || Luogu P4137 Rmq Problem / mex

    题面:Rmq Problem / mex 题解: 先离散化,然后插一堆空白,大体就是如果(对于以a.data<b.data排序后的A)A[i-1].data+1!=A[i].data,则插一个空 ...

  2. BZOJ.4771.七彩树(可持久化线段树)

    BZOJ 考虑没有深度限制,对整棵子树询问怎么做. 对于同种颜色中DFS序相邻的两个点\(u,v\),在\(dfn[u],dfn[v]\)处分别\(+1\),\(dfn[LCA(u,v)]\)处\(- ...

  3. BZOJ 4771: 七彩树 可持久化线段树+树链的并

    这个思路挺有意思的 ~ 利用树链的并来保证每个颜色只贡献一次,然后用可持久化线段树维护 code: #include <set> #include <cstdio> #incl ...

  4. BZOJ4771七彩树——可持久化线段树+set+树链的并+LCA

    给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点.每个节点都被染上了某一种颜色,其中第i个节 点的颜色为c[i].如果c[i]=c[j],那么我们认为点i和点j拥有相同的颜色.定义dept ...

  5. BZOJ 3483 SGU505 Prefixes and suffixes(字典树+可持久化线段树)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3483 [题目大意] 给出一些串,同时给出m对前缀后缀,询问有多少串满足给出的前缀后缀模 ...

  6. bzoj 2653 二分答案+可持久化线段树

    首先离散化,然后我们知道如果对于一个询问的区间[l1,r1],[l2,r2],我们二分到一个答案x,将[l1,r2]区间中的元素大于等于x的设为1,其余的设为-1,那么如果[l1,r1]的最大右区间和 ...

  7. BZOJ 3439 Kpm的MCpassword Trie树+可持久化线段树

    题目大意:给定n个字符串,对于每一个字符串求以这个字符串为后缀的字符串中第k小的编号 首先将字符串反转 那么就变成了对于每一个字符串求以这个字符串为前缀的字符串中第k小的编号 然后考虑对字符串排序 那 ...

  8. 归并树 划分树 可持久化线段树(主席树) 入门题 hdu 2665

    如果题目给出1e5的数据范围,,以前只会用n*log(n)的方法去想 今天学了一下两三种n*n*log(n)的数据结构 他们就是大名鼎鼎的 归并树 划分树 主席树,,,, 首先来说两个问题,,区间第k ...

  9. 主席树[可持久化线段树](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 ...

随机推荐

  1. let,const

    - 让webstorm支持ES6语法:file-setting-languages&frameworks-javascript-右侧选择ES6 - let定义变量没有预解释且不能重复定义,在定 ...

  2. java操作mongodb工具类

    新建maven项目 pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="ht ...

  3. Git的基本操作命令

    Git的基本命令 ## Git命令行 ### 查看配置 ```d git config user.name //查看用户名 git config user.email //查看邮箱 ``` ### 全 ...

  4. day02-css

    技术分析 HTML的块标签: div标签: 默认占一行,自动换行 span标签: 内容显示在同一行 CSS概述: Cascading Style Sheets : 层叠样式表 主要用作用: 用来美化我 ...

  5. [Windows] GIF编辑器

    目录 1. 功能简介 2. 下载地址 3. 使用教程 3.1. 其他视频转gif的方案 1. 功能简介 可以自定义录屏位置.区域大小做GIF 可以编辑GIF.压缩GIF等 可以将视频转换成GIF 可以 ...

  6. 解决SVN异常 cleanup failed

    winndows上偶尔使用SVN的时候就会整出一些有的没的问题,比如"cleanup failed to process the following paths previous opera ...

  7. QTextStream写文件中文乱码解决办法

    1.首先把Qt Creator的编辑器设置为使用 UTF-8:   工具-->选项-->文本编辑器-->行为,在右侧选项界面找到文件编码选项,设置为 UTF-8.2.使用 QText ...

  8. php session之redis存储

    前提:redis已安装好. php代码: <?php ini_set("session.save_handler", "redis"); ini_set( ...

  9. 微信小程序-没有找到 node_modules 目录的解决办法

    初次在微信开发者工具构建npm 没有找到 node_modules 目录的解决办法 第一步:设置-->项目设置-->使用npm模块 第二步:右键目录下miniprogram-->终端 ...

  10. Leetcode_395. Longest Substring with At Least K Repeating Characters_[Devide and Conquer]

    题目链接 对一个字符串,找出一个最长的子串长度,这个子串中所有字符出现的次数至少为k. 1.滑动窗口 一开始把题目看成了,子串中每个字符至多出现k次.如果是这样,那么是一道典型的滑动窗口的题目. 然而 ...