BZOJ3779 重组病毒

14 篇文章 0 订阅

诶,卡常数差评

左边的YihAN_Z神犇拿指针版写了一遍就过了,我写的数组版就不过……

有关LCT维护子树信息的讲解可以看这里

用LCT维护链上颜色段数和x的子树内所有的点到x的颜色段数之和,实现题里的操作,查询的时候把根到x的链信息和x的子树信息合并起来就可以出解了

因为就是一个普通的LCT,所以复杂度是n log n 的

这里就说一下每个变量是什么,具体怎么维护就看代码把
LCT上有虚边和实边,实边就是儿子认爹、爹也认儿子,虚边就是儿子认爹爹不认儿子

splay子树即由实边连成的子树

LCT子树即所有在LCT结构中在x以下的子树

fa,son:LCT的父亲儿子结构变量

st,tp:在splay之前下传上边所有标记用的栈

rev,ch:翻转标记和染色标记

siz:LCT上的子树大小

Siz:x的虚子树中在lct上直接连x的部分的大小

SIZ:x的splay上子树大小

v:x的颜色

lv,rv:x的splay子树中最左端和最右端的颜色

cols:x的splay子树在原树上代表的链的颜色段数

coll,colr:x的LCT子树里的所有的点都在原树里走到x的splay子树里最左(右)的点所经过的颜色段数和

Col:x的虚子树中在lct上直接连x的部分在原树上都走到x所经过的颜色段数和

sCol:在计算x的Col的过程中,由于x在原树上的儿子与x颜色相同而没有被计算的颜色段数量

sumsCol:x的splay子树内所有点的sCol值之和

sumCol:x的splay子树内所有点的Col值之和

然后只要在link,access,update和染色的时候好好维护那些量就可以啦!

即使是嘴上说说好像也不是很轻松愉快啊!

可是由于update函数过长,常数过大,跑的挺慢的_-

时隔10个月,我终于贴了一份指针版的代码A了这道题

#include <cstdio>
#include <algorithm>
#define N 100005
#define int long long
using namespace std;
typedef long long LL;
struct Node {
    Node *ch[2],*pa;
    int dir() { return pa->ch[0]==this ? 0 : pa->ch[1]==this ? 1 : -1; }
    int siz,xsiz,rsiz;
    int v,lv,rv,coll,colr,cols,Col,sumCol,sCol,sumsCol;
    int color_mark;
    bool rev_mark;
    Node();
    void rev();
    void color(int);
    void pushdown();
    void maintain();
    void update(Node*,int);
}*null=new Node(),p[N];
Node :: Node():rev_mark(false),color_mark(0),xsiz(0) {
    pa=ch[0]=ch[1]=null;
    siz=null?1:0;
    rsiz=siz;
    v=lv=rv=coll=colr=cols=Col=sumCol=sCol=sumsCol=0;
}
void Node :: rev() {
    if(this==null) return ;
    swap(ch[0],ch[1]);
    swap(lv,rv);
    swap(coll,colr);
    rev_mark=!rev_mark;
    return ;
}
void Node :: color(int x) {
    if(this==null) return ;
    v=lv=rv=color_mark=x;
    coll=colr=siz+sumCol+sumsCol;
    cols=1;
    Col+=sCol;
    sumCol+=sumsCol;
    sCol=sumsCol=0;
    return ;
}
void Node :: pushdown() {
    if(rev_mark) {
        ch[0]->rev();
        ch[1]->rev();
        rev_mark=false;
    }
    if(color_mark) {
        ch[0]->color(color_mark);
        ch[1]->color(color_mark);
        color_mark=0;
    }
    return ;
}
void Node :: maintain() {
    if(this==null) return ;
    siz=ch[0]->siz+ch[1]->siz+1;
    rsiz=ch[0]->rsiz+ch[1]->rsiz+1+xsiz;
    lv=rv=v;
    coll=colr=Col+1;
    cols=1;
    sumCol=Col+ch[0]->sumCol+ch[1]->sumCol;
    sumsCol=sCol+ch[0]->sumsCol+ch[1]->sumsCol;
    if(ch[0]!=null) {
        coll+=ch[0]->coll+ch[0]->cols*(xsiz+1);
        int tmp=ch[1]->cols+(ch[1]->lv!=v);
        colr+=ch[0]->colr+tmp*ch[0]->rsiz;
        if(ch[0]->rv==v) coll-=1+xsiz, colr-=ch[0]->rsiz;
    }
    if(ch[1]!=null) {
        colr+=ch[1]->colr+ch[1]->cols*(xsiz+1);
        int tmp=ch[0]->cols+(ch[0]->rv!=v);
        coll+=ch[1]->coll+tmp*ch[1]->rsiz;
        if(ch[1]->lv==v) colr-=1+xsiz, coll-=ch[1]->rsiz;
    }
    if(ch[0]!=null)
        cols+=ch[0]->cols-(ch[0]->rv==v),
        lv=ch[0]->lv;
    if(ch[1]!=null)
        cols+=ch[1]->cols-(ch[1]->lv==v),
        rv=ch[1]->rv;
    return ;
}
void Node :: update(Node* o,int k) {
    xsiz+=k*o->rsiz;
    Col+=k*o->coll;
    if(v!=o->lv) Col+=k*o->rsiz;
    else sCol+=k*o->rsiz;
    return ;
}
void Rotate(Node* o,int d) {
    Node* k=o->ch[d^1]; int d2;
    o->ch[d^1]=k->ch[d]; k->ch[d]->pa=o;
    k->ch[d]=o;
    o->maintain(), k->maintain();
    if(~(d2=o->dir())) o->pa->ch[d2]=k;
    k->pa=o->pa, o->pa=k;
    return ;
}
void To_pushdown(Node* o) {
    static Node* q[N];
    int top=0;
    while(~(o->dir())) q[++top]=o, o=o->pa;
    q[++top]=o;
    while(top) q[top--]->pushdown();
    return ;
}
void Splay(Node* o) {
    To_pushdown(o);
    int d;
    while(~(d=o->dir())) {
        if(o->pa->dir()==d) Rotate(o->pa->pa,d^1);
        Rotate(o->pa,d^1);
    }
    return ;
}
void Access(Node* o) {
    Node* p=null;
    while(o!=null) {
        Splay(o);
        o->update(o->ch[1],1), o->update(p,-1);
        o->ch[1]=p, o->maintain();
        p=o;
        o=o->pa;
    }
    return ;
}
void Move_to_root(Node* o) {
    Access(o), Splay(o);
    o->rev();
    return ;
}
void Link(Node* x,Node* y) {
    Move_to_root(x);
    Move_to_root(y);
    x->pa=y;
    y->update(x,1);
    y->maintain();
    return ;
}
int n,m,T;
void request(Node* o) {
    Access(o), Splay(o);
    int ans=o->Col+1,anst=o->xsiz+1,ans2=o->cols;
    ans+=anst*(ans2-1);
    printf("%.10f\n",1.0*ans/anst);
    return ;
}
void release(Node* o) {
    Access(o), Splay(o);
    o->color(++T);
    return ;
}
void recenter(Node* o) {
    Move_to_root(o);
    o->color(++T);
    return ;
}
main() {
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++) p[i].color(++T);
    for(int i=1;i<n;i++) {
        int x,y;
        scanf("%lld%lld",&x,&y);
        Link(p+x,p+y);
    }
    Move_to_root(p+1);
    while(m--) {
        char mode[10];
        int x;
        scanf("%s%lld",mode,&x);
        if(mode[2]=='Q') request(p+x);
        else if(mode[2]=='L') release(p+x);
        else recenter(p+x);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值