bzoj3779-重组病毒

一颗\(n\)个点的树,初始颜色都不同,初始根为1。定义一个点到根的代价为这个点到根路径上不同颜色个数。有\(m\)个操作,分三种:

  • 将一个点到根路径上的所有点颜色改为一种新的颜色
  • 询问一个点的子树的所有点到根的代价和
  • 对点\(x\)进行操作1后把根换成\(x\)

\(n,m\le 10^5\)

分析

那个修改看着是不是很像access操作呢!换根操作之前要先access就是link-cut tree的makeroot呀!

首先,令1的深度为1,那么初始所有点的代价就是他们的深度。我们用lct来维护这个树的结构,支持换根即可。所以一切的关键都集中在了access上。

access其实就是实虚边切换的操作。注意到代价其实就是点到根路径上虚边个数+1,所以如果我们把一条实边改成虚边,那么整颗子树的答案加一,虚边改实边整个子树答案减一,用线段树维护一下dfs序就好啦。

等等!我们不是换了根吗,怎么用dfs序维护子树呢?画一画图,设当前点为\(x\),发现:

  • \(root=x\),那么就是整个树
  • \(root\)\(x\)的子树中,那么就是整棵树除去\(x\)的root那颗子树
  • 否则就是原来的子树

所以dfs序依然可以维护,只是要分情况讨论一下。

代码

#include<cstdio>
#include<cctype>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long giant;
int read() {
    int x=0,f=1;
    char c=getchar();
    for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
    for (;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
const int maxn=1e5+10;
const int maxj=18;
int n,m,root=1,first[maxn],second[maxn],dft=0,f[maxn][maxj],dep[maxn];
struct node {
    int ch[2],fa;
    bool rev;
} t[maxn];
struct SGT {
    giant t[maxn<<2],tag[maxn<<2];
    void push(int x,int L,int mid,int R) {
        if (!tag[x]) return;
        tag[x<<1]+=tag[x],tag[x<<1|1]+=tag[x];
        t[x<<1]+=(mid-L+1)*tag[x];
        t[x<<1|1]+=(R-mid)*tag[x];
        tag[x]=0;
    }
    void add(int x,int L,int R,int l,int r,giant d) {
        if (L==l && R==r) {
            tag[x]+=d;
            t[x]+=(R-L+1)*d;
            return;
        }
        int mid=(L+R)>>1;
        push(x,L,mid,R);
        if (r<=mid) add(x<<1,L,mid,l,r,d); else 
        if (l>mid) add(x<<1|1,mid+1,R,l,r,d); else
        add(x<<1,L,mid,l,mid,d),add(x<<1|1,mid+1,R,mid+1,r,d);
        t[x]=t[x<<1]+t[x<<1|1];
    }
    void add(int l,int r,giant d) {
        if (l>r) return;
        add(1,1,n,l,r,d);
    }
    giant query(int x,int L,int R,int l,int r) {
        if (L==l && R==r) return t[x];
        int mid=(L+R)>>1;
        push(x,L,mid,R);
        if (r<=mid) return query(x<<1,L,mid,l,r); 
        if (l>mid) return query(x<<1|1,mid+1,R,l,r); else 
        return query(x<<1,L,mid,l,mid)+query(x<<1|1,mid+1,R,mid+1,r);
    }
    giant query(int l,int r) {
        if (l>r) return 0ll;
        return query(1,1,n,l,r);
    }
} sgt;
vector<int> g[maxn];
void add(int x,int y) {g[x].push_back(y);}
void dfs(int x,int fa) {
    first[x]=++dft;
    dep[x]=dep[fa]+1;
    f[x][0]=fa;
    sgt.add(first[x],first[x],dep[x]);
    for (int v:g[x]) if (v!=fa) t[v].fa=x,dfs(v,x);
    second[x]=dft;
}
int jump(int x,int y) {
    if (!y) return x;
    for (int j=0;j<maxj;++j) if ((y>>j)&1) x=f[x][j];
    return x;
}
bool insub(int x,int y) {
    return first[x]<=first[y] && first[y]<=second[x];
}
double query(int x) {
    if (root==x) {
        return (double)sgt.query(1,n)/n;
    } else if (insub(x,root)) {
        int p=jump(root,dep[root]-dep[x]-1);
        double ret=sgt.query(1,first[p]-1)+sgt.query(second[p]+1,n);
        ret/=(double)(first[p]-1+n-second[p]);
        return ret;
    } else {
        return (double)sgt.query(first[x],second[x])/(second[x]-first[x]+1);
    }
}
bool rson(int x) {
    return t[t[x].fa].ch[1]==x;
}
bool isroot(int x) {
    return !x || t[t[x].fa].ch[rson(x)]!=x;
}
void push(int x) {
    if (t[x].rev) {
        swap(t[x].ch[0],t[x].ch[1]);
        if (t[x].ch[0]) t[t[x].ch[0]].rev^=true;
        if (t[x].ch[1]) t[t[x].ch[1]].rev^=true;
        t[x].rev=false;
    }
}
void down(int x) {
    if (!isroot(x)) down(t[x].fa);
    push(x);
}
void rotate(int x) {
    int f=t[x].fa,d=rson(x),c=t[x].ch[d^1];
    if (!isroot(f)) t[t[f].fa].ch[rson(f)]=x;
    if (c) t[c].fa=f;
    t[x].fa=t[f].fa,t[f].fa=x,t[f].ch[d]=c,t[x].ch[d^1]=f;
}
void splay(int x) {
    down(x);
    while (!isroot(x)) {
        if (isroot(t[x].fa)) rotate(x); else {
            if (rson(x)==rson(t[x].fa)) rotate(t[x].fa),rotate(x); else 
            rotate(x),rotate(x);
        }
    }
}
void change(int x,giant d) {
    if (x==root) sgt.add(1,n,d); else 
    if (insub(x,root)) {
        int p=jump(root,dep[root]-dep[x]-1);
        sgt.add(1,first[p]-1,d);
        sgt.add(second[p]+1,n,d);
    } else {
        sgt.add(first[x],second[x],d);
    }
}
int left(int x) {
    for (push(x);t[x].ch[0];x=t[x].ch[0],push(x));
    return x;
}
void ace(int x) {
    for (int last=0;x;x=t[last=x].fa) {
        splay(x);
        giant tmp=sgt.query(first[x],first[x]);
        if (t[x].ch[1]) change(left(t[x].ch[1]),1);
        if (last) change(left(last),-1);
        t[x].ch[1]=last;
    }
}
void cent(int x) {
    root=x;
    splay(x);
    t[x].rev^=true;
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("test.in","r",stdin);
    freopen("my.out","w",stdout);
#endif
    n=read(),m=read();
    for (int i=1;i<n;++i) {
        int x=read(),y=read();
        add(x,y),add(y,x);
    }
    dfs(1,1);
    for (int j=1;j<maxj;++j) for (int i=1;i<=n;++i) f[i][j]=f[f[i][j-1]][j-1];
    while (m--) {
        static char ord[20];
        scanf("%s",ord);
        int x=read();
        if (ord[2]=='Q') {
            double ans=query(x);
            printf("%.10lf\n",ans);
        } else {
            ace(x);
            if (ord[2]=='C') cent(x);
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/owenyu/p/6912377.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值