[SDOI2017] 树点涂色 [LCT/线段树/LCA]

Problem

Bob有一棵 n 个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。

定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。

Bob可能会进行这几种操作:

1 x
把点 x 到根节点的路径上所有的点染上一种没有用过的新颜色。

2 x y
求 x 到 y 的路径的权值。

3 x
在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。

Bob一共会进行 m 次操作

Solution

emm我并没做出来
我一开始想着怎么维护让一段区间的点的所有为0的儿子变成1,然后这思路有各种问题
我果然还是太年轻

题解传送门:https://www.luogu.org/blog/newuser/solution-p3703

重点在于怎么快速进行操作1,操作2/3的思路都差不多
树剖的做法是暴力更改??恕我没看懂代码...

一种优美的做法是LCT的Access本质思想应用?
果然每种专题都有对本质思想的应用啊..

以及我一开始的查询其实是num[a]+num[b]-num[lca(a,b)]-num[fa[lca(a,b)]]类似这样的
然后这玩意是错的
所以为什么呢,请看下面的代码把ww

Code

// Code by ajcxsu
// Problem: color on the tree

#include<bits/stdc++.h>
using namespace std;

template <typename T> inline void gn(T &x) {
    char ch=getchar();
    x=0;
    while(ch<'0' || ch>'9') ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0', ch=getchar();
} 

const int N=1e5+10, M=2e5+10;
int n,m;
int h[N], to[M], nexp[M], p=1;
inline void ins(int a,int b) {
    nexp[p]=h[a], h[a]=p, to[p]=b, p++;
}

namespace ST {
    #define ls x<<1
    #define rs x<<1|1
    int num[N*6], t[N*6];
    inline void pud(int x) {
        if(!t[x]) return;
        t[ls]+=t[x], t[rs]+=t[x];
        num[ls]+=t[x], num[rs]+=t[x];
        t[x]=0;
    }
    void updata(int x, int l, int r, int xl, int xr, int d) {
        pud(x);
        if(xl<=l && r<=xr) {
            num[x]+=d, t[x]+=d;
            return;
        }
        int mid=(l+r)>>1;
        if(xl<=mid) updata(ls, l, mid, xl, xr, d);
        if(xr>mid) updata(rs, mid+1, r, xl, xr, d); // 使得区间一定有交集,避免无谓的搜索 
        num[x]=max(num[ls], num[rs]);
    }
    int query(int x, int l, int r, int xl, int xr) {
        pud(x);
        if(xl<=l && r<=xr) return num[x];
        int mid=(l+r)>>1;
        if(xr<=mid) return query(ls, l, mid, xl, xr);
        else if(xl>mid) return query(rs, mid+1, r, xl, xr);
        else return max(query(ls, l, mid, xl, mid), query(rs, mid+1, r, mid+1, xr));
    }
}

int dep[N], son[N], fa[N], siz[N];
int dfn[N], top[N], idx;
int nl[N], nr[N];

struct Node *nil;
struct Node {
    int id;
    Node *f, *ch[2];
    Node (int id=0):id(id) {
        f=ch[0]=ch[1]=nil;
    }
    bool nroot() {
        return f->ch[0]==this || f->ch[1]==this;
    }
    int dir() {
        if(!nroot()) return -1;
        return f->ch[1]==this;
    }
} *nd[N];
void ini() {
    nil=new Node;
    nil->f=nil->ch[0]=nil->ch[1]=nil;
}

void rot(Node *x) {
    Node *y=x->f, *z=y->f;
    int d=x->dir();
    y->ch[d]=x->ch[!d];
    if(x->ch[!d]!=nil) x->ch[!d]->f=y;
    x->f=z;
    if(y->dir()!=-1) z->ch[y->dir()]=x;
    x->ch[!d]=y, y->f=x;
}

void splay(Node *x) {
    Node *y; 
    while(x->nroot()) {
        y=x->f;
        if(!y->nroot()) rot(x);
        else if(y->dir() == x->dir()) rot(y), rot(x);
        else rot(x), rot(x);
    }
}

Node *find(Node *r) {
    while(r->ch[0]!=nil) r=r->ch[0];
    return r;
}

void access(Node *x) {
    Node *y=nil;
    int a;
    while(x!=nil) {
        splay(x);
        if(x->ch[1]!=nil) {
            a=find(x->ch[1])->id;
            ST::updata(1, 1, n, nl[a], nr[a], 1);
        }
        x->ch[1]=y;
        if(y!=nil) {
            a=find(y)->id;
            ST::updata(1, 1, n, nl[a], nr[a], -1);
        }
        y=x, x=x->f;
    }
}

void dfs1(int x, int k) {
    dep[x]=k, siz[x]=1;
    for(int u=h[x];u;u=nexp[u])
        if(!dep[to[u]]) {
            fa[to[u]]=x, nd[to[u]]->f=nd[x], dfs1(to[u], k+1);
            siz[x]+=siz[to[u]];
            if(siz[to[u]]>siz[son[x]]) son[x]=to[u];
        }
}
void dfs2(int x, int t) {
    nl[x]=dfn[x]=++idx;
    ST::updata(1, 1, n, dfn[x], dfn[x], dep[x]);
    top[x]=t;
    if(son[x]) dfs2(son[x], t);
    for(int u=h[x];u;u=nexp[u])
        if(!dfn[to[u]]) dfs2(to[u], to[u]);
    nr[x]=idx;
}
int lca(int x,int y) {
    while(top[x]!=top[y]) {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) return y;
    return x; 
}

int main() {
    ini();
    gn(n), gn(m);
    int u,v;
    for(int i=1;i<n;i++) gn(u), gn(v), ins(u,v), ins(v,u);
    for(int i=1;i<=n;i++) nd[i]=new Node(i);
    dfs1(1,1), dfs2(1,1);
    int a,b,c,d;
    while(m--) {
        gn(a), gn(b);
        if(a==1) access(nd[b]);
        else if(a==2) {
            gn(c), d=lca(b,c);
            printf("%d\n", ST::query(1,1,n,dfn[b],dfn[b])+ST::query(1,1,n,dfn[c],dfn[c])
                            -2*ST::query(1,1,n,dfn[d],dfn[d])+1); // 由于两个都减去了d所在的splay,因此得把它加回来
            // 由于fa[d]和d有可能是在同一个splay,所以仍然导致少算上一个splay 
        }
        else if(a==3) printf("%d\n", ST::query(1,1,n,nl[b],nr[b]));
    }
    return 0;
}

本文链接:https://acxblog.site/archives/sol-luogu-3703.html
文章采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。