bzoj 4771: 七彩树 树链的并+可持久化线段树

题目大意:

给定一颗树,询问树中某个点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;
}

转载于:https://www.cnblogs.com/Skyminer/p/6556815.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值