bzoj4771 七彩树

七彩树

Time Limit: 5 Sec Memory Limit: 256 MB

Description

给定一棵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的所有点中出现了多少种本质不同的颜色
。请写一个程序,快速回答这些询问。

Input

第一行包含一个正整数T(1<=T<=500),表示测试数据的组数。
每组数据中,第一行包含两个正整数n(1<=n<=100000)和m(1<=m<=100000),表示节点数和询问数。
第二行包含n个正整数,其中第i个数为ci,分别表示每个节点的颜色。
第三行包含n-1个正整数,其中第i个数为fi+1,表示节点i+1的父亲节点的编号。
接下来m行,每行两个整数x(1<=x<=n)和d(0<=d<n),依次表示每个询问。
输入数据经过了加密,对于每个询问,如果你读入了x和d,那么真实的x和d分别是x xor last和d xor last,
其中last表示这组数据中上一次询问的答案,如果这是当前数据的第一组询问,那么last=0。
输入数据保证n和m的总和不超过500000。

Output

对于每个询问输出一行一个整数,即答案。

Sample Input

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

Sample Output

1
2
3
1
1
2
1
1

本来是个离线题的,就是说你按深度插入每个点。(当然要先树剖一下啦!)
单独维护每一个颜色,两个相邻的点就在lca的地方减一就好了。
但是他是个在线就的很毒。。。
我们以毒攻毒,用可持久化线段树。。。


#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
struct lpl{
    int fa, dfn, top, son, size, deep;
}a[maxn];
struct ld{
    int id, dfn;
    inline bool operator < (const ld &A)const{return dfn < A.dfn;}
};
struct lpd{
    int sum, ls, rs;
}node[10000010];
int n, m, c[maxn];
int lastans, last, num, tot, opt, cnt, mxd, root = 1;
int d[maxn], rt[maxn * 5], ot[maxn], dp[maxn];
vector<int> point[maxn];
set<ld> s[maxn];
set<ld>::iterator iter, op, ed;
queue<int> q;

namespace HLD{
    void dfs1(int t, int fa){
        a[t].size = 1; int tmp = 0; mxd = max(mxd, a[t].deep);
        for(int i = point[t].size() - 1; i >= 0; --i){
            int now = point[t][i]; if(now == fa) continue;
            a[now].deep = a[t].deep + 1; dfs1(now, t);
            a[t].size += a[now].size;
            if(tmp < a[now].size){tmp = a[now].size; a[t].son = now;}
        }
    }

    void dfs2(int t, int fa){
        a[t].dfn = ++num;
        if(a[t].son){a[a[t].son].top = a[t].top; dfs2(a[t].son, t);}
        for(int i = point[t].size() - 1; i >= 0; --i){
            int now = point[t][i]; if(now == fa || now == a[t].son) continue;
            a[now].top = now; dfs2(now, t);
        }
    }

    inline int LCA(int x1, int x2){
        if(a[a[x1].top].deep < a[a[x2].top].deep) swap(x1, x2);
        while(a[x1].top != a[x2].top){
            x1 = a[a[x1].top].fa;
            if(a[a[x1].top].deep < a[a[x2].top].deep) swap(x1, x2);
        }
        return (a[x1].deep > a[x2].deep) ? x2 : x1;
    }
}

namespace SGT{
    inline void update(int t){node[t].sum = node[node[t].ls].sum + node[node[t].rs].sum;}

    void build(int &t, int l, int r){
        t = ++cnt; node[t].sum = 0; if(l == r) return;
        int mid = (l + r) >> 1;
        build(node[t].ls, l, mid); build(node[t].rs, mid + 1, r);
    }

    void Modify(int &t, int l, int r, int pos, int val){
        t = ++cnt; node[t] = node[last];
        if(l == r){node[t].sum += val; return;}
        int mid = (l + r) >> 1;
        if(pos <= mid){last = node[last].ls; Modify(node[t].ls, l, mid, pos, val);}
        else{last = node[last].rs; Modify(node[t].rs, mid + 1, r, pos, val);}
        update(t);
    }

    int Query(int t, int l, int r, int ql, int qr){
        if(l == ql && r == qr) return node[t].sum;
        int mid = (l + r) >> 1;
        if(qr <= mid) return Query(node[t].ls, l, mid, ql, qr);
        if(mid < ql) return Query(node[t].rs, mid + 1, r, ql, qr);
        return Query(node[t].ls, l, mid, ql, mid) + Query(node[t].rs, mid + 1, r, mid + 1, qr);
    }
}

template<typename T>
inline void read(T &x){
    x = 0; static char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)){x = x * 10 + c - '0'; c = getchar();}
}

template<typename T>
void write(T x){
    if(x / 10) write(x / 10);
    putchar(x % 10 + '0');
}

inline void putit(){
    read(n); read(m); lastans = 0; mxd = 0;
    for(int i = 1; i <= n; ++i) {
        point[i].clear(); s[i].clear();
        a[i].son = 0; read(c[i]);
    }
    for(int f, i = 1; i < n; ++i){
        read(f); a[i + 1].fa = f;
        point[f].push_back(i + 1);
    }
}

inline void prepare(){
    a[root].deep = 1; num = 0; memset(rt, 0, sizeof(rt));
    HLD::dfs1(root, 0); a[1].top = 1; HLD::dfs2(root, 0);
    q.push(root); int now; tot = 0;
    while(!q.empty()){
        now = q.front(); q.pop(); d[++tot] = now;
        for(int i = point[now].size() - 1; i >= 0; --i) q.push(point[now][i]);
    }
    cnt = 0; SGT::build(rt[0], 1, n); opt = 0;
    bool qian, hou;
    for(int color, i = 1; i <= tot; ++i){
        color = c[d[i]];
        iter = s[color].insert((ld){d[i], a[d[i]].dfn}).first;
        qian = (iter != s[color].begin()); iter++; hou = (iter != s[color].end()); iter--;
        if(qian){iter--; op = iter; iter++;}
        if(hou){iter++; ed = iter; iter--;}
        if(qian && hou){
            last = rt[opt];
            SGT::Modify(rt[++opt], 1, n, a[HLD::LCA(op -> id, ed -> id)].dfn, 1);
        }
        if(qian){
            last = rt[opt];
            SGT::Modify(rt[++opt], 1, n, a[HLD::LCA(op -> id, iter -> id)].dfn, -1);
        }
        if(hou){
            last = rt[opt];
            SGT::Modify(rt[++opt], 1, n, a[HLD::LCA(iter -> id, ed -> id)].dfn, -1);
        }
        last = rt[opt]; SGT::Modify(rt[++opt], 1, n, iter -> dfn, 1);
        ot[d[i]] = opt; dp[a[d[i]].deep] = d[i];
    }
}

inline void workk(){
    int x, D;
    while(m--){
        read(x); read(D);
        x ^= lastans; D ^= lastans;
        D += a[x].deep; D = min(D, mxd);
        D = ot[dp[D]];
        lastans = SGT::Query(rt[D], 1, n, a[x].dfn, a[x].dfn + a[x].size - 1);
        write(lastans); putchar(10);
    }
}

int main()
{
    int T; read(T);
    while(T--){
        putit();
        prepare();
        workk();
    }
    return 0;
}

原文地址:https://www.cnblogs.com/LLppdd/p/9780186.html

时间: 2024-11-02 22:55:09

bzoj4771 七彩树的相关文章

bzoj4771 七彩树 dfs序+主席树+树链的并

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4771 题解 一道不错的树链并的基础练习题. 如果不是树,而是一个数组的话,对于给定区间内的不同颜色数,我们可以维护一个 \(pre_{i, j}\) 表示前 \(i\) 个最后一个 \(j\) 出现的位置.那么答案就是 \(pre_{r, j} \geq l\) 的 \(j\) 的个数,用主席树维护,第一维 \(i\),第二维 \(pre_i, j\),维护的是数量就可以了.(当然这个问题还

BZOJ 4771: 七彩树

4771: 七彩树 Time Limit: 5 Sec  Memory Limit: 256 MBSubmit: 289  Solved: 87[Submit][Status][Discuss] Description 给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点.每个节点都被染上了某一种颜色,其中第i个节 点的颜色为c[i].如果c[i]=c[j],那么我们认为点i和点j拥有相同的颜色.定义depth[i]为i节点与根节点的距离 ,为了方便起见,你可以认为树上相邻的两个点之间的

4771: 七彩树 主席树

题意:给定一棵树  每个结点有一个颜色 然后又m个询问 询问:x d   问x的子树内不超过dep[x]+d 深度的子树结点一共有多少个颜色? 1.可以先将问题简化为问整个子树内有多少个不同的颜色  暴力解法树套树  但是可以用一个技巧来快速维护: 一个颜色一个颜色地处理  把所有相同颜色的点按照dfs序排序,每个点给自己的位置贡献1,相邻的两个点给lca贡献−1.然后只要区间内存在这种颜色,则其子树内的权值和必定为1.那么只需要这样子染好所有颜色之后询问子树和. 所以如果问题是这样的话只要一个

Noip前的大抱佛脚----数据结构

数据结构 线段树 注意:空间开4倍 神奇标记 From8.26 Test_zsy(CPU监控) 如果一个点权为\(val\)的点被打上了\((a,b)\)标记,那么他的实际点权为\(max(a+val,b)\) 干啥滴? 标记不下放 区间加标记不下放,维护区间max或者最大值 方法是当前\(tag\)维护当前区域标记,\(t\)维护左右儿子的\(max+tag[now]\),并没有快多少,如果仍然忘记见提交记录 并查集 维护二分图 并查集每个点维护是否要改颜色,然后按秩合并/按大小合并即可 实际

bzoj4771 -- dfs序+倍增+主席树

先考虑没有深度限制的情况. 先将每个节点的权值设为1,对于颜色相同且在dfs序中最近的2个点,用倍增求出lca并将它的权值减一.然后子树中不同的颜色种数就是子树的权值和了. 有深度限制时,考虑以深度为时间建立主席树. 将每个点按深度排序,枚举一遍.对每种颜色开一个set,枚举到一个点时将它在dfs序中的位置加入set中并更新就可以了. 时间复杂度O(T*(nlogn+mlogn)) 代码: #include<iostream> #include<cstdio> #include&l

HDU 6203 ping ping ping [LCA,贪心,DFS序,BIT(树状数组)]

题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=6203] 题意 :给出一棵树,如果(a,b)路径上有坏点,那么(a,b)之间不联通,给出一些不联通的点对,然后判断最少有多少个坏点. 题解 :求每个点对的LCA,然后根据LCA的深度排序.从LCA最深的点对开始,如果a或者b点已经有点被标记了,那么continue,否者标记(a,b)LCA的子树每个顶点加1. #include<Bits/stdc++.h> using namespace std;

HDU 5542 The Battle of Chibi dp+树状数组

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5542 题意:给你n个数,求其中上升子序列长度为m的个数 可以考虑用dp[i][j]表示以a[i]结尾的长度为j的上升子序列有多少 裸的dp是o(n2m) 所以需要优化 我们可以发现dp的第3维是找比它小的数,那么就可以用树状数组来找 这样就可以降低复杂度 #include<iostream> #include<cstdio> #include<cstring> #include

【树4】二叉树的遍历

简介 遍历二叉树就是按照某种顺序,将树中的结点都枚举一遍,且每个结点仅仅访问一次.因为树不是线性的结构,遍历不像线性表那样简单,因此他的遍历需要特点的算法来完成. 从某种角度讲,对二叉树的遍历就是将树形结构转换为线性结构的操作. 二叉树的遍历方法主要有如下几种: 先序遍历:先访问root结点,再先序遍历左子树,再先序遍历右子树. 中序遍历:先中序遍历左子树,再访问root结点,再中序遍历右子树. 后序遍历:先后序遍历左子树,再后序遍历右子树,再访问root结点. 层遍历:从上到下,从左到右,一层

关于左偏树的一些东东

大概所有的预备知识这里都有https://baike.baidu.com/item/%E5%B7%A6%E5%81%8F%E6%A0%91/2181887?fr=aladdin 例题1:洛谷 P3377 [模板]左偏树(可并堆) 383通过 1.2K提交 题目提供者HansBug 站长团 标签 难度提高+/省选- 时空限制1s / 128MB 提交 讨论 题解 最新讨论更多讨论 加了路径压缩就WA,路过dal… 左偏树用指针写会MLE吗..… m,n写反了也可以过,数据有… 哪位大神有pbds库