bzoj 3779 重组病毒

sol:

题面看吐了,实际上操作就是

1.access

2.makeroot

3.查询一个点到根上有多少虚边

 

1. 和 2. 可以 access ,每次断掉的轻边子树 -1,新连的轻边子树 +1 ,用线段树维护一下子树即可,类似树剖套线段树的方法换根

顺便复习一下换根

1.如果当前点在根的子树里,直接加

2.如果当前点就是根,整棵树加

3.如果根在当前点的子树里,从根爬到 x 下面的一个儿子 p ,修改除 p 子树外所有点

#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read()
{
    int x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if(ch == '-') f = -f;
    for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
    return x * f;
}
const int maxn = 100010; 
int n,m,rt;
int first[maxn],to[maxn << 1],nx[maxn << 1],cnt;
inline void add(int u,int v)
{
    to[++cnt] = v;
    nx[cnt] = first[u];
    first[u] = cnt;
}
inline void ins(int u,int v){add(u,v);add(v,u);}
int par[maxn][24],ind[maxn],oud[maxn],size[maxn],dep[maxn],reh[maxn],dfn;
inline void dfs(int x)
{
    size[x] = 1;ind[x] = ++dfn;reh[dfn] = dep[x];
    for(int i=1;i<=23;i++)par[x][i] = par[par[x][i - 1]][i - 1];
    for(int i=first[x];i;i=nx[i])
    {
        if(to[i] == par[x][0])continue;
        par[to[i]][0] = x;
        dep[to[i]] = dep[x] + 1;
        dfs(to[i]);size[x] += size[to[i]];
    }oud[x] = dfn;
}
LL seg[maxn << 2],tag[maxn << 2];
inline void pushup(int x){seg[x] = seg[x << 1] + seg[x << 1 | 1];}
inline void pushdown(int x,int l,int r)
{
    if(tag[x])
    {
        int mid = (l + r) >> 1;
        tag[x << 1] += tag[x];tag[x << 1 | 1] += tag[x];
        seg[x << 1] += (mid - l + 1) * tag[x];seg[x << 1 | 1] += (r - mid) * tag[x];
        tag[x] = 0;
    }
}
inline void build(int x,int l,int r)
{
    if(l == r)
    {
        seg[x] = reh[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(x << 1,l,mid);build(x << 1 | 1,mid + 1,r);
    pushup(x);
}
inline void update(int x,int l,int r,int L,int R,LL v)
{
    if(L <= l && r <= R)
    {
        tag[x] += v;
        seg[x] += (r - l + 1) * v;
        return;
    }
    pushdown(x,l,r);
    int mid = (l + r) >> 1;
    if(L <= mid)update(x << 1,l,mid,L,R,v);
    if(R > mid)update(x << 1 | 1,mid + 1,r,L,R,v);
    pushup(x);
}
inline LL query(int x,int l,int r,int L,int R)
{
    if(L <= l && r <= R)return seg[x];
    int mid = (l + r) >> 1;LL ans = 0;
    pushdown(x,l,r);
    if(L <= mid)ans += query(x << 1,l,mid,L,R);
    if(R > mid)ans += query(x << 1 | 1,mid + 1,r,L,R);
    return ans;
}
inline int isin(int x,int rt){return ind[rt] >= ind[x] && ind[rt] <= oud[x];}
inline int getson(int x,int rt)
{
    int t = dep[x] - dep[rt] - 1;
    for(int i=23;~i;i--)if(t & (1 << i))x = par[x][i];
    return x;
}
inline void Modify(int x,LL v)
{
    if(x == rt)update(1,1,n,1,n,v);
    else if(isin(x,rt))
    {
        int vn = getson(rt,x);
        if(ind[vn] > 1)update(1,1,n,1,ind[vn] - 1,v);
        if(oud[vn] < n)update(1,1,n,oud[vn] + 1,n,v);
    }
    else update(1,1,n,ind[x],oud[x],v);
}
inline double Query(int x)
{
    if(x == rt)return 1.0 * query(1,1,n,1,n) / n;
    else if(isin(x,rt))
    {
        double ans = 0;
        int vn = getson(rt,x);
        if(ind[vn] > 1)ans += query(1,1,n,1,ind[vn] - 1);
        if(oud[vn] < n)ans += query(1,1,n,oud[vn] + 1,n);
        return ans / (n - size[vn]);
    }
    else return (1.0 * query(1,1,n,ind[x],oud[x]) / size[x]);
}
#define ls ch[x][0]
#define rs ch[x][1]
int fa[maxn],ch[maxn][2],rev[maxn],st[maxn],top;
inline int isroot(int x){return ch[fa[x]][1] != x && ch[fa[x]][0] != x;}
inline void pushdown(int x)
{
    if(rev[x])
    {
        swap(ls,rs);
        rev[ls] ^= 1;
        rev[rs] ^= 1;
        rev[x] ^= 1;
    }
}
inline void rotate(int x)
{
    int y = fa[x],z = fa[y];
    int l = (ch[y][1] == x),r = l ^ 1;
    if(!isroot(y))ch[z][ch[z][1] == y] = x;
    fa[x] = z;fa[ch[x][r]] = y;fa[y] = x;
    ch[y][l] = ch[x][r];ch[x][r] = y;
}
inline void splay(int x)
{
    st[top = 1] = x;
    for(int i=x;!isroot(i);i=fa[i])st[++top] = fa[i];
    for(int i=top;i;i--)pushdown(st[i]);
    while(!isroot(x))
    {
        int y = fa[x],z = fa[y];
        if(!isroot(y))
        {
            if(ch[z][0] == y ^ ch[y][0] == x)rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
}
inline int fd(int x)
{
    pushdown(x);
    while(ls)x = ls,pushdown(x);
    return x;
}
inline void access(int x)
{
    for(int y=0;x;rs=y,y=x,x=fa[x])
    {
        splay(x);
        int tmp = fd(rs);if(rs) Modify(tmp,1);
        tmp = fd(y);if(y) Modify(tmp,-1);
    }
}
inline void makeroot(int x)
{
    access(x);
    splay(x);
    rev[x] ^= 1;
    rt = x;
}
int main()
{
    //freopen("20.in","r",stdin);
    //freopen("gen.out","w",stdout);
    n = read(),m = read();rt = 1;
    for(int i=2;i<=n;i++)ins(read(),read());
    dep[1] = 1;dfs(1);
    build(1,1,n);
    for(int i=1;i<=n;i++)fa[i] = par[i][0];
    char opt[50];
    while(m--)
    {
        scanf("%s",opt + 1);int x = read();
        if(opt[3] == 'L')access(x);
        else if(opt[3] == 'C')makeroot(x);
        else printf("%.10lf\n",Query(x));
    }
}
View Code

 

转载于:https://www.cnblogs.com/Kong-Ruo/p/10250140.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值