题目大意:

给定一颗树,询问树中某个点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. 【BZOJ3698】XWW的难题 有上下界的最大流

    [BZOJ3698]XWW的难题 Description XWW是个影响力很大的人,他有很多的追随者.这些追随者都想要加入XWW教成为XWW的教徒.但是这并不容易,需要通过XWW的考核.XWW给你出了 ...

  2. 爬虫入门【5】PyQuery简介

    PyQuery 目前最新的版本是1.3,基于最新版本进行介绍. 主要根据PyQuery的官方文档进行了更新. from pyquery import PyQuery as pq from lxml i ...

  3. 九度OJ 1188:约瑟夫环 (约瑟夫环)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:1891 解决:817 题目描述: N个人围成一圈顺序编号,从1号开始按1.2.3......顺序报数,报p者退出圈外,其余的人再从1.2.3 ...

  4. Webpack探索【2】--- 安装、项目初始化、webpack.config.js配置文件

    本文主要讲安装.项目初始化.webpack.config.js配置文件方面的内容.

  5. Linux备份和回复mysql数据库

    备份:mysqldump -u root -p密码 数据库名>/home/data.bak   mysqldump -u root -p密码 数据库名.表名>/home/data.bak ...

  6. cordova 获取地理位置

    第一步,引入插件 cordova plugin add cordova-plugin-geolocation 第二步, <!DOCTYPE html> <html> <h ...

  7. Android TextView文字过多时通过滚动条显示多余内容

    方法一: TextView文字过多,显示不全,怎么办?我们可以为Textview添加滚动条. <TextView android:id="@+id/bus_detail_content ...

  8. C#聚合运算方法

    Aggregate 对集合值执行自定义聚合运算 Average 计算集合平均值 Count 对集合的元素惊醒计数,还可以仅对满足某一谓词函数的元素进行计数 LongCount 对大型集合中的元素进行计 ...

  9. Linux 下 Crontab 命令使用详解 定时任务

    一.  Crontab 介绍 crontab命令的功能是在一定的时间间隔调度一些命令的运行. 1.1 /etc/crontab 文件 在/etc文件夹下有一个crontab文件,这里存放有系统运行的一 ...

  10. 第2条:遵循PEP8风格指南

    <Python Enhancement Proposal #8>(8号Python增强提案)又叫PEP8,它是针对Python代码格式而编订的风格指南. 尽管可以在保证语法正确的前提下随意 ...