[BZOJ3779] 重组病毒

博客讲述了如何解决BZOJ3779问题,涉及树的染色操作和换根操作。通过将颜色数转化为虚边个数来简化问题,使用LCT(Link-Cut Tree)和dfs序线段树进行维护。文章讨论了如何处理树的Access过程和换根操作,特别是当换根时如何在线段树上进行维护,以及处理不同查询类型的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这题考了两遍 QAQAQAQ

第一遍还是我懵懂无知的时候,不知道换根怎么搞,就打的跟染色那题似的用LCT直接维护。

第二遍临近noi一个月的现在,LCT+dfs序线段树就可以了,但是手残打跪了。

题目抽象为这个模型

有一棵n个节点的树,每个节点有一个颜色,初始每个节点颜色不相同,且以节点1为根。定义每个点的权值为这个点到根的路径上不同颜色的个数。现在进行m次操作,每次操作为下列三种之一:
1、将x到当前根路径上的所有点染成一种新的颜色;
2、将x到当前根路径上的所有点染成一种新的颜色,并且把这个点设为新的根;
3、查询以x为根的子树中所有点权值的平均值。

好吧也不算抽象,其实这是另一个题目描述囧

第一步转换:任意一个点到根的路径上,相同的颜色必然是连续的一段,于是重新定义颜色数目为一个点到根的路径上虚边个数+1

于是操作一就是Access过程咯,就是在Access 过程中进行虚实边切换的时候,假设一个节点v要接为u的右儿子,那么u的右儿子所代表的子树整体会将权值 +1,而 v 所代表的子树整体会将权值 -1。

这一步用LCTdfs序线段树都可以维护。

第二步转换:我们发现换根比较棘手。。因为LCT换跟是没法维护子树的QAQ,于是我们选择在线段树上维护。

利用dfs序的性质,设新的根为root,当前查询节点为u,那么 uroot 存在三种关系。
1. x=root,查询整棵树
2. root 不在 x 的子树内,直接查询以1为根时x的子树即可
3. rootx 的子树内,设 px 的亲儿子中包含 root 的节点,那么需要查询的就是整棵树去除掉 p 这棵子树的结果得到的答案了。

需要注意的是“代表的子树”这个东西,需要记录一棵splay中最左和最右的节点分别是谁。

然后祝打的愉快

// by ztx
// [bzoj] 3779重组病毒

#include <cstdio>
#include <algorithm>

#define  maxn  100010LL

int CH,NEG;
template <typename Tp> inline void read(Tp&ret) {
    ret=NEG=0;while (CH=getchar(),CH<'!') ;
    if(CH=='-')NEG=true,CH=getchar();
    while(ret=ret*10+CH-'0',CH=getchar(),CH>'!') ;
    if (NEG) ret=-ret;
}

typedef long long ll ;
typedef double lf ;

int n ;

/* EDGE */

struct FST { int to , next ; } e[maxn<<1] ;
int star[maxn] = {0} , tote = 1 ;
inline void AddEdge(int u,int v) { e[++tote].to=v;e[tote].next=star[u];star[u]=tote; }
inline void Link(int u,int v) { AddEdge(u,v);AddEdge(v,u); }

/*  LCT  */

int c[2][maxn] = {0} , fa[maxn] , left[maxn] , right[maxn] ;
bool rev[maxn] = {0} ;

#define  l(o)  c[0][o]
#define  r(o)  c[1][o]
#define  f(o)  fa[o]

inline bool Isrt(int x) { return !f(x) || (l(f(x))!=x && r(f(x))!=x) ; }
inline void Reverse(int x) {
    rev[x] ^= 1 ;
    std::swap(left[x],right[x]) ;
}
inline void Clear(int x) {
    if (!x || !rev[x]) return ;
    rev[x] = false ;
    if (l(x)) Reverse(l(x)) ;
    if (r(x)) Reverse(r(x)) ;
    std::swap(l(x),r(x)) ;
}
inline void Maintain(int x) {
    left[x] = right[x] = x ;
    if (l(x)) left[x] = left[l(x)] ;
    if (r(x)) right[x] = right[r(x)] ;
}
inline void Rot(int x,int d) {
    int y = f(x) , z = f(y) ;
    if (l(z)==y) l(z)=x ;
    else if (r(z)==y) r(z)=x ;
    f(x)=z , f(y)=x , f(c[d][x])=y ;
    c[!d][y]=c[d][x] , c[d][x]=y ;
    Maintain(y) ;
}
inline void Splay(int x) {
    int y , z ;
    Clear(x) ;
    while (!Isrt(x)) {
        y = f(x) , z = f(y) ;
        Clear(z) , Clear(y) , Clear(x) ;
        if (Isrt(y)) Rot(x,l(y)==x) ;
        else if (l(z)==y) {
            if (l(y)==x) Rot(y,1) ;
            else Rot(x,0) ;
            Rot(x,1) ;
        } else {
            if (r(y)==x) Rot(y,0) ;
            else Rot(x,1) ;
            Rot(x,0) ;
        }
    }
    Maintain(x) ;
}

#undef  l
#undef  r
#undef  f

/* SegmentTree */

#define  maxt  400010LL
#define  l(o)  (o<<1)
#define  r(o)  (o<<1|1)
#define  M     (L+R>>1)
#define  Left  l(o),L,M
#define  Right r(o),M+1,R
#define  Update(o)  sum[o] = sum[l(o)]+sum[r(o)]

int tott , ql , qr ;
ll val[maxn] , sum[maxt] , add[maxt] = {0} , qw , qa ;

inline void Build(int o,int L,int R) {
    if (L == R) { sum[o] = val[L] ; return ; }
    Build(Left) ; Build(Right) ;
    Update(o) ;
}

inline void NodeAdd(int o,int L,int R,ll w) {
    sum[o] += w*(R-L+1) ; add[o] += w ;
}

inline void Pushdown(int o,int L,int R) {
    if (!o || !add[o]) return ;
    if (l(o)) NodeAdd(Left,add[o]) ;
    if (r(o)) NodeAdd(Right,add[o]) ;
    add[o] = 0 ;
}

inline void Modify(int o,int L,int R) {
    if (ql<=L && R<=qr) {
        NodeAdd(o,L,R,qw) ;
        return ;
    }
    Pushdown(o,L,R) ;
    if (ql<=M) Modify(Left) ;
    if (qr>M) Modify(Right) ;
    Update(o) ;
}

inline void SegModify(int L,int R,ll w) {
    if (L > R) return ;
    ql = L , qr = R , qw = w ;
    Modify(1,1,n) ;
}

inline void Query(int o,int L,int R) {
    if (ql<=L && R<=qr) {
        qa += sum[o] ; return ;
    }
    Pushdown(o,L,R) ;
    if (ql<=M) Query(Left) ;
    if (qr>M) Query(Right) ;
    Update(o) ;
}

inline ll SegQuery(int L,int R) {
    if (L > R) return 0 ;
    ql = L , qr = R , qa = 0 ;
    Query(1,1,tott) ;
    return qa ;
}

#undef  l
#undef  r

/* ST */

#define  maxk  18LL

int dep[maxn] = {0} , d[maxk][maxn] = {0} ;

inline void ST_init() {
int i , k ;
    for (k = 1 ; k < maxk ; k ++ )
        for (i = 1 ; i <= n ; i ++ )
            d[k][i] = d[k-1][d[k-1][i]] ;
}

inline int NotSon(int u,int rt) {
int k ;
    for (k = maxk-1 ; k >= 0 ; k -- )
        if (dep[d[k][rt]] > dep[u]) rt = d[k][rt] ;
    return rt ;
}

// DFN

int Beg[maxn] , End[maxn] ;

void dfs(int u) {
    Beg[u] = ++tott ;
    d[0][u] = fa[u] ;
    val[tott] = val[Beg[fa[u]]]+1 ;
    dep[u] = dep[fa[u]]+1 ;
    left[u] = right[u] = u ;
    for (int p = star[u] ; p ; p = e[p].next)
        if (e[p].to != fa[u]) fa[e[p].to]=u,dfs(e[p].to) ;
    End[u] = tott ;
}

//

#define  l(o)  c[0][o]
#define  r(o)  c[1][o]

int rtnow = 1 ;

inline void SubModify(int u,int w) {
    if (!u) return ;
    if (u == rtnow) SegModify(1,tott,w) ;
    else if (Beg[u]<=Beg[rtnow] && End[rtnow]<=End[u]) {
        int p = NotSon(u,rtnow) ;
        SegModify(1,Beg[p]-1,w) ;
        SegModify(End[p]+1,tott,w) ;
    } else {
        SegModify(Beg[u],End[u],w) ;
    }
}

inline void Modify(int u) {
    for (int v = 0 ; u ; v = u , u = fa[u]) {
        Splay(u) ;
        SubModify(left[v],-1) ;
        SubModify(left[r(u)],1) ;
        r(u) = v ;
        Maintain(u) ;
    }
}

inline void Query(int u) {
ll sum , siz ;
    if (!u) return ;
    if (u == rtnow) {
        sum = SegQuery(1,n) ;
        siz = n ;
    } else if (Beg[u]<=Beg[rtnow] && End[rtnow]<=End[u]) {
        int p = NotSon(u,rtnow) ;
        sum = SegQuery(1,Beg[p]-1) + SegQuery(End[p]+1,tott) ;
        siz = n-(End[p]-Beg[p]+1) ;
    } else {
        sum = SegQuery(Beg[u],End[u]) ;
        siz = End[u]-Beg[u]+1 ;
    }
    printf("%.10lf\n", (lf)sum/(lf)siz) ;
}

int main() {
int m , i , cmd , u , v ;
 //   #define READ
    #ifdef READ
        freopen("c.in","r",stdin) ;
        freopen("c.out","w",stdout) ;
    #endif
    read(n) , read(m) ;
    for (i = 1 ; i < n ; i ++ ) {
        read(u) , read(v) ;
        Link(u,v) ;
    }
    val[Beg[tott=0]=0] = 0 , fa[1] = 0 ;
    dfs(1) ;
    Build(1,1,tott) ;
    ST_init() ;
    for (i = 1 ; i <= m ; i ++ ) {
        while (cmd=getchar(),cmd<'!') ;
        getchar(),cmd=getchar();
        while (u=getchar(),u>'!') ;
        read(u) ;
        if (cmd == 'L') Modify(u) ;
        if (cmd == 'C') {
            Modify(u) ;
            Splay(u) ;
            Reverse(u) ;
            rtnow = u ;
        }
        if (cmd == 'Q') Query(u) ;
    }
    #ifdef READ
        fclose(stdin) , fclose(stdout) ;
    #else
        getchar() , getchar() ;
    #endif
    return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值