LCT模板题

bzoj3779. 重组病毒

LCT模板题(虚)

前方高能,你需要(看一些线段树+dfs序+splay涨姿势)

题目大意:给定一棵树,初始每个点都有一个颜色,支持三种操作:
1.将某个点到根的路径上所有点染上一种新的颜色
2.将某个点到根的路径上所有点染上一种新的颜色,然后把根设为这个点
3.定义一个点的代价为这个点到根路径上颜色的种类数,求某个点子树中所有点代价的平均值

答记者问

答什么答,抄别人模板还好意思BB


先自己试试?

bzoj3779
隐藏题

我这么说你们可能不懂,看代码!!!

总结:

1.线段树
2.LCT
3.dfs序

下发文件


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;

const int N = 1e5 + 6;
const int Nlg = 18;

int n, m;//n,m见题 
int to[N+N], next[N+N], end[N], tms;//4个是前向星
int fi[N], se[N], rat[N], dep[N], fa[N], up[N][Nlg];// fi:子树中最小dfs序值, se:子树中最大dfs序值 
    //rat : dfs序值对应的树上编号 dep :深度 fa :父节点 up:LCA 
namespace seg//线段树部分 
{
    LL cnt[N<<2];
    int tag[N<<2], siz[N<<2];

    void pup(int x){cnt[x] = cnt[x+x] + cnt[x+x+1];}
    void ptg(int x, int tg){tag[x] += tg, cnt[x] += siz[x] * tg;} 
    void pdw(int x){if(tag[x]) ptg(x+x, tag[x]), ptg(x+x+1, tag[x]), tag[x]=0;}

    void cons(int x, int l, int r)
    {
        cnt[x] = tag[x] = 0, siz[x] = r-l+1;
        if(l==r) {cnt[x] = dep[rat[l]]; return;}
        int mid = (l+r) >> 1;
        cons(x+x, l, mid), cons(x+x+1, mid+1, r);
        pup(x);
    }

    int opl, opr, opty, opg;
    LL opans;

    // 0 : modify 1 : query
    void step(int x, int l, int r)
    {
        if(opl<=l && r<=opr)
        {
            if(opty) opans += cnt[x]; else ptg(x, opg);
            return;
        }
        int mid = (l+r)>>1; pdw(x);
        if(opl<=mid) step(x+x, l, mid);
        if(opr>mid) step(x+x+1, mid+1, r);
        if(!opty) pup(x);
    }

    int root;

    int ances(int x, int y){ return fi[x] <= fi[y] && fi[y] <= se[x]; }
    int jump(int x, int h) { for(int i=0; h; h>>=1, i++) if(h&1) x=up[x][i]; return x;}

    void deal(int x)
    {
        if(x == root) opl = 1, opr = n, step(1, 1, n);
        else if(ances(x, root))
        {
            x = jump(root, dep[root]-dep[x]-1);
            if(fi[x]>1) opl = 1, opr = fi[x]-1, step(1, 1, n);
            if(se[x]<n) opl = se[x]+1, opr = n, step(1, 1, n);
        }
        else opl = fi[x], opr = se[x], step(1, 1, n);
    }

    void modify(int x, int op) { opty = 0, opg = op, deal(x); }
    LL query(int x) { return opty = 1, opans = 0, deal(x), opans; }

    int count(int x)
    {
        if(x == root) return n;
        if(ances(x, root)) return x = jump(root, dep[root]-dep[x]-1),  n-(se[x]-fi[x]+1);
        return se[x]-fi[x]+1;
    }
}

namespace lct
{

    struct rc
    {
        int rev, l, r, val;
        rc *c[2], *f;

        int d(){return f->c[1]==this;}
        int rt(){return f->c[1]!=this && f->c[0]!=this;}
        void sc(rc *x, int d){c[d]=x, x->f=this;}

        void pup(){l = c[0]->val ? c[0]->l : val, r = c[1]->val ? c[1]->r : val;}
        void re(){rev^=1, swap(c[0], c[1]), swap(l, r);}
        void pdw(){if(rev) c[0]->re(), c[1]->re(), rev=0;}
    }nil[N];

    #define T(x) (nil+x) 

    void zig(rc *x)
    {
        int d = x->d();
        rc *p = x->f;
        p->sc(x->c[!d], d), p->pup();
        if(p->rt()) x->f=p->f; else p->f->sc(x, p->d());
        x->sc(p, !d);
    }

    void splay(rc *x)
    {
        for(rc *y; !x->rt(); )
        {
            if(y=x->f, !y->rt()) y->f->pdw();
            y->pdw(), x->pdw();
            if(!y->rt()) x->d() ^ y->d() ? zig(x) : zig(y);
            zig(x);
        }
        x->pup();
    }

    void access(rc *x)
    {
        rc *v = x, *y = nil;
        for(int u; x!=nil; )
        {
            splay(x), x->pdw();
            if(x->c[1]!=nil) u=x->c[1]->l, seg :: modify(u, 1);
            if(y!=nil) u=y->l, seg :: modify(u, -1);
            x->sc(y, 1), x->pup(), y = x, x = x->f;
        }
        splay(v);
    }

    void evert(rc *x){access(x), x->re();}

    void cons()//找根 
    {
        for(int i=1; i<=n; i++)
        T(i)->f = T(fa[i]), T(i)->l = T(i)->r = T(i)->val = i, 
        T(i)->c[0] = T(i)->c[1] = nil;
    }

    void upload(int x){ access(T(x)); }
    void croot(int x){ evert(T(x)); }
}

namespace rec
{
    //No.1 前向星存 
    void init()
    {
        scanf("%d%d", &n, &m);
        for(int i=1, x, y; i<n; i++)
        {
            scanf("%d%d", &x, &y);
            to[++tms]=y, next[tms]=end[x], end[x]=tms;
            to[++tms]=x, next[tms]=end[y], end[y]=tms;
        }
    }
    //No.2 dfs
    void dfs(int x)
    {
        fi[x] = ++fi[0], rat[fi[0]] = x, up[x][0] = fa[x];
        for(int i=0; up[up[x][i]][i]; ++i) up[x][i+1] = up[up[x][i]][i];
        for(int p=end[x], v; p; p=next[p]) if(v=to[p], v!=fa[x])
        fa[v]=x, dep[v]=dep[x]+1, dfs(v);
        se[x] = fi[0];

    }
    //No.3 建树
    void prep()
    {
        dep[1] = 1, dfs(1);
        seg :: cons(1, 1, n);
        seg :: root = 1;
    }
    //No.4 输出 
    void answ()
    {
        for(int i=1, t, x; i<=m; i++)
        {
            scanf("%d%d", &t, &x);
            if(t==1) lct :: upload(x);
            else if(t==2) lct :: croot(x), seg :: root = x;
            else printf("%.12lf\n", seg :: query(x) / double(seg :: count(x)));
        }
    }

}

int main()//这个main是不是很良心哇 
{

    rec :: init();
    rec :: prep();
    lct :: cons();
    rec :: answ();

    return 0;
}

人生经验

如果你解决本题有什么人生经验(程序错误,我的错误,不懂的地方,奇奇怪怪的优化想法)可以留言偶!,我会选一些挂上。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值