题目大意:

给定一颗树,询问树中某个点x的子树中与其距离不超过d的所有点中本质不同的颜色数

强制在线

题解:

一下午终于把这道题叉掉了。

写了三个算法,前两个都是错的,后一个是%的网上大爷们的题解.

首先我们发现这道题有一个特点:没有修改操作 !!

这就使我们能够对颜色进行预处理。

所以我们可以考虑分别枚举每个颜色,对其去重后更新树上的信息。

所以我们可以考虑树链的并,我们可以对每种颜色都做一遍树链的并

容易发现复杂度仍然是\(O(nlogn)\)的

但是这样我们只求出来的每个节点子树中不同的颜色的个数

并没有满足对深度的限制

弱弱的我想到这里就继续不下去了,不知道下面改怎么写,YY了两个算法

第一个算法打着打着感觉不对,(Ctrl+A) + (Backspace)

第二个算法打出来了调样例,手模一下发现算法是错的.(Alt+F4)


去%了大爷们的题解:

我们把所有的点按照深度动态地进行树链的并.

这样,我们就发现我们实际上可以求出每一个深度到树根中不同颜色的种类

但是我们发现我们单单考虑了深度的一个边界还没有结束.

我们还需要限制深度另一个边界和在x子树中的限制

我们发现其实这两个限制等价于dfs序在x的子树dfs序范围之间.

所以。。。在深度上用线段树可持久化即可...

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 100010;
struct Edge{
int to,next;
}G[maxn<<2];
int head[maxn],cnt;
void add(int u,int v){
G[++cnt].to = v;
G[cnt].next = head[u];
head[u] = cnt;
}
int dfn[maxn],dfs_clock,son[maxn],siz[maxn];
int dep[maxn],top[maxn],fa[maxn],oud[maxn]; #define v G[i].to
void dfs(int u){
siz[u] = 1;
for(int i = head[u];i;i=G[i].next){
if(v == fa[u]) continue;
fa[v] = u;
dep[v] = dep[u] + 1;
dfs(v);
siz[u] += siz[v];
if(siz[son[u]] < siz[v]) son[u] = v;
}
}
void dfs(int u,int tp){
top[u] = tp;dfn[u] = ++dfs_clock;
if(son[u]) dfs(son[u],tp);
for(int i = head[u];i;i=G[i].next){
if(v == fa[u] || v == son[u]) continue;
dfs(v,v);
}
oud[u] = dfs_clock;
}
#undef v
inline int lca(int u,int v){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u,v);
u = fa[top[u]];
}return dep[u] < dep[v] ? u : v;
}
int col[maxn],n;
namespace Trp{
struct Node{
Node *ch[2];
int dfn,siz,fix,id;
void update(){
siz = ch[0]->siz + ch[1]->siz + 1;
}
}mem[maxn<<2],*it,*null,*root[maxn];
inline void init(){
it = mem;null = it++;
null->ch[0] = null->ch[1] = 0;null->id = -1;
null->dfn = null->siz = 0;
}
inline Node* newNode(int x,int i){
Node *p = it++;p->ch[0] = p->ch[1] = null;
p->dfn = x;p->fix = rand();
p->siz = 1;p->id = i;
return p;
}
void rotate(Node* &p,int k){
Node *y = p->ch[k^1];
p->ch[k^1] = y->ch[k];
y->ch[k] = p;
p->update();y->update();
p = y;
}
void insert(Node* &p,int x,int id){
if(p == null) p = newNode(x,id);
else{
insert(p->ch[p->dfn < x],x,id);
p->update();
if(p->ch[p->dfn<x]->fix < p->fix)
rotate(p,p->dfn > x);
}
}
inline int find(int k,Node *root){
Node *p = root;
if(k < 1 || k > p->siz) return -1;
while(p != null){
if(p->ch[0]->siz + 1 == k) return p->id;
if(p->ch[0]->siz + 1 > k) p = p->ch[0];
else k -= p->ch[0]->siz + 1,p = p->ch[1];
}assert(0);
}
inline int rank(int d,Node* root){
int ret = 1;Node *p = root;
while(p != null){
if(p->dfn < d) ret += p->ch[0]->siz + 1,p = p->ch[1];
else p = p->ch[0];
}return ret;
}
}
namespace seg{
struct Node{
Node* ch[2];
int val;
void update(){
val = ch[0]->val + ch[1]->val;
}
}mem[maxn*100],*it,*null,*root[maxn];
inline void init(){
it = mem;null = it++;
null->ch[0] = null->ch[1] = null;
null->val = 0;root[0] = null;
}
Node* insert(Node *rt,int l,int r,int pos,int w){
Node *p = it++;*p = *rt;
if(l == r){
p->val += w;
return p;
}
int mid = l+r >> 1;
if(pos <= mid) p->ch[0] = insert(p->ch[0],l,mid,pos,w);
else p->ch[1] = insert(p->ch[1],mid+1,r,pos,w);
p->update();return p;
}
int query(Node *p,int l,int r,int L,int R){
if(L <= l && r <= R) return p->val;
int mid = l+r >> 1;
if(R <= mid) return query(p->ch[0],l,mid,L,R);
if(L > mid) return query(p->ch[1],mid+1,r,L,R);
return query(p->ch[0],l,mid,L,R) + query(p->ch[1],mid+1,r,L,R);
}
}
int q[maxn],l,r,mx;
inline void bfs(){
l = 0;r = -1;q[++r] = 1;
while(l <= r){
int u = q[l++],x = Trp::rank(dfn[u],Trp::root[col[u]]),y,z;
mx = max(mx,dep[u]);
seg::root[dep[u]] = seg::insert(seg::root[dep[q[l-2]]],1,n,dfn[u],1);
Trp::insert(Trp::root[col[u]],dfn[u],u);
y = Trp::find(x-1,Trp::root[col[u]]);z = Trp::find(x+1,Trp::root[col[u]]);
if(y != -1 && z != -1){
int lc = lca(y,z);
seg::root[dep[u]] = seg::insert(seg::root[dep[u]],1,n,dfn[lc],1);
}
if(y != -1){
int lc = lca(y,u);
seg::root[dep[u]] = seg::insert(seg::root[dep[u]],1,n,dfn[lc],-1);
}
if(z != -1){
int lc = lca(z,u);
seg::root[dep[u]] = seg::insert(seg::root[dep[u]],1,n,dfn[lc],-1);
}
for(int i = head[u];i;i=G[i].next){
int v = G[i].to;
if(v == fa[u]) continue;
q[++r] = v;
}
}
}
inline void init(){
memset(head,0,sizeof head);cnt = 0;
memset(son,0,sizeof son);
memset(siz,0,sizeof siz);
dfs_clock = 0;mx = 0;
}
int main(){
int T;read(T);
srand(6613);
while(T--){
init();
seg::init();Trp::init();
int m;read(n);read(m);
for(int i=0;i<=n;++i){
Trp::root[i] = Trp::null;
seg::root[i] = seg::null;
}
for(int i=1;i<=n;++i) read(col[i]);
for(int i=2;i<=n;++i){
read(fa[i]);add(fa[i],i);
}dfs(1);dfs(1,1);
bfs();
int ans = 0;
int x,d;
while(m--){
read(x);read(d);
x ^= ans;d ^= ans;
int de = dep[x] + d;
if(de > mx) de = mx;
ans = seg::query(seg::root[de],1,n,dfn[x],oud[x]);
printf("%d\n",ans);
}
}
getchar();getchar();
return 0;
}

bzoj 4771: 七彩树 树链的并+可持久化线段树的更多相关文章

  1. BZOJ - 2588 Spoj 10628. Count on a tree (可持久化线段树+LCA/树链剖分)

    题目链接 第一种方法,dfs序上建可持久化线段树,然后询问的时候把两点之间的所有树链扒出来做差. #include<bits/stdc++.h> using namespace std; ...

  2. [BZOJ 3207] 花神的嘲讽计划Ⅰ【Hash + 可持久化线段树】

    题目链接:BZOJ - 3207 题目分析 先使用Hash,把每个长度为 k 的序列转为一个整数,然后题目就转化为了询问某个区间内有没有整数 x . 这一步可以使用可持久化线段树来做,虽然感觉可以有更 ...

  3. BZOJ.3218.a + b Problem(最小割ISAP 可持久化线段树优化建图)

    BZOJ UOJ 首先不考虑奇怪方格的限制,就是类似最大权闭合子图一样建图. 对于奇怪方格的影响,显然可以建一条边\((i\to x,p_i)\),然后由\(x\)向\(1\sim i-1\)中权值在 ...

  4. [BZOJ 4771]七彩树(可持久化线段树+树上差分)

    [BZOJ 4771]七彩树(可持久化线段树+树上差分) 题面 给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点.每个节点都被染上了某一种颜色,其中第i个节点的颜色为c[i].如果c[i] ...

  5. 计蒜客 38229.Distance on the tree-1.树链剖分(边权)+可持久化线段树(区间小于等于k的数的个数)+离散化+离线处理 or 2.树上第k大(主席树)+二分+离散化+在线查询 (The Preliminary Contest for ICPC China Nanchang National Invitational 南昌邀请赛网络赛)

    Distance on the tree DSM(Data Structure Master) once learned about tree when he was preparing for NO ...

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

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

  7. Tsinsen A1505. 树(张闻涛) 倍增LCA,可持久化线段树,DFS序

    题目:http://www.tsinsen.com/A1505 A1505. 树(张闻涛) 时间限制:1.0s   内存限制:512.0MB    总提交次数:196   AC次数:65   平均分: ...

  8. [学习笔记] 可持久化线段树&主席树

    众所周知,线段树是一个非常好用也好写的数据结构, 因此,我们今天的前置技能:线段树. 然而,可持久化到底是什么东西? 别急,我们一步一步来... step 1 首先,一道简化的模型: 给定一个长度为\ ...

  9. [LOJ2310][APIO2017]斑斓之地——可持久化线段树

    题目链接: [APIO2017]斑斓之地 将不是河流的格子染成白色,是河流的格子染成黑色,那么连通块数就是白色格子数$-1*2$的联通白色格子数$-2*1$的联通白色格子数$+2*2$的联通白色格子数 ...

随机推荐

  1. 【BZOJ3784】树上的路径 点分治序+ST表

    [BZOJ3784]树上的路径 Description 给定一个N个结点的树,结点用正整数1..N编号.每条边有一个正整数权值.用d(a,b)表示从结点a到结点b路边上经过边的权值.其中要求a< ...

  2. Webpack探索【7】--- sourceMap、自动构建刷新功能详解

    本文主要讲sourceMap.自动构建刷新功能.

  3. NOIP考前划水

    NOIP考前划水 君指先跃动の光は.私の一生不変の信仰に.唯私の超電磁砲永世生き! 要开始背配置了? 3行不谢. (setq c-default-style "awk") (glo ...

  4. 我的Android进阶之旅------> Android为TextView组件中显示的文本添加背景色

    通过上一篇文章 我的Android进阶之旅------> Android在TextView中显示图片方法 (地址:http://blog.csdn.net/ouyang_peng/article ...

  5. Python 中奇妙的下划线

    单个下划线(_) 通常有三种用法: 在python解释器: 单个下划线代表上次在交互解释期对话中(控制台)执行的结果.这种情况在标准的CPython解释器中首次被实现,接下来这种习惯也被保持下来: & ...

  6. Maven+SSM框架(Spring+SpringMVC+MyBatis) - Hello World(转发)

    [JSP]Maven+SSM框架(Spring+SpringMVC+MyBatis) - Hello World 来源:http://blog.csdn.net/zhshulin/article/de ...

  7. linux sort按照指定列排序

    sort怎样按指定的列排序0000 27189 41925425065f 15 419254250663 7 419254250675 5 419254250691 76 419254250693 2 ...

  8. 阿里云 rails nginx 配置https访问

    1.申请免费型dv ssl证书:https://common-buy.aliyun.com/?spm=a2c4e.11155515.0.0.7zzvOZ&commodityCode=cas#/ ...

  9. Scala window下安装

    第一步:Java 设置 检测方法前文已说明,这里不再描述. 如果还为安装,可以参考我们的Java 开发环境配置. 接下来,我们可以从 Scala 官网地址 http://www.scala-lang. ...

  10. php 通用redis类

    php 通用redis类 <?php /** * redis操作类 * 说明,任何为false的串,存在redis中都是空串. * 只有在key不存在时,才会返回false. * 这点可用于防止 ...