其他分享
首页 > 其他分享> > 弹飞绵羊

弹飞绵羊

作者:互联网

弹飞绵阳,洛谷。花了2000ms,o2后1000ms 

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define R register int
#define I inline void
#define lc c[x][0]
#define rc c[x][1]
using namespace std;
const int N=1e6;
inline int in()
{
    int s=0;
    char ch = getchar();
    while(ch < '0' || ch > '9')ch = getchar();
    while(ch >= '0' && ch <= '9')s = (s<<1) + (s<<3) + (ch^48),ch = getchar();
    return s;
}
int f[N],c[N][2],v[N],s[N],st[N],go[N],va[N];
bool r[N];
inline bool nroot(R x) //判断节点是否为一个Splay的根(与普通Splay的区别1)
{
    return c[f[x]][0]==x||c[f[x]][1]==x;
}//原理很简单,如果连的是轻边,他的父亲的儿子里没有它
I pushup(R x) //上传信息
{
    s[x]=s[lc]+s[rc]+v[x];
}
I pushr(R x)
{
    R t=lc;    //翻转操作
    lc=rc;
    rc=t;
    r[x]^=1;
}
I pushdown(R x) //判断并释放懒标记
{
    if(r[x])
    {
        if(lc)
            pushr(lc);
        if(rc)
            pushr(rc);
        r[x]=0;
    }
}
I rotate(R x) //一次旋转
{
    R y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k];
    if(nroot(y))
        c[z][c[z][1]==y]=x;
    c[x][!k]=y;
    c[y][k]=w;//额外注意if(nroot(y))语句,此处不判断会引起致命错误(与普通Splay的区别2)
    if(w)
        f[w]=y;
    f[y]=x;
    f[x]=z;
    pushup(y);
}
I splay(R x) //只传了一个参数,因为所有操作的目标都是该Splay的根(与普通Splay的区别3)
{
    R y=x,z=0;
    st[++z]=y;//st为栈,暂存当前点到根的整条路径,pushdown时一定要从上往下放标记(与普通Splay的区别4)
    while(nroot(y))
        st[++z]=y=f[y];
    while(z)
        pushdown(st[z--]);
    while(nroot(x))
    {
        y=f[x];
        z=f[y];
        if(nroot(y))
            rotate((c[y][0]==x)^(c[z][0]==y)?x:y);
        rotate(x);
    }
    pushup(x);
}
/*当然了,其实利用函数堆栈也很方便,代替上面的手工栈,就像这样
I pushall(R x){
    if(nroot(x))pushall(f[x]);
    pushdown(x);
}*/
I access(R x) //访问
{
    for(R y=0; x; x=f[y=x])
        splay(x),rc=y,pushup(x);
}
I makeroot(R x) //换根
{
    access(x);
    splay(x);
    pushr(x);
}
int findroot(R x) //找根(在真实的树中的)
{
    access(x);
    splay(x);
    while(lc)
        pushdown(x),x=lc;
    splay(x);
    return x;
}
I split(R x,R y) //提取路径
{
    makeroot(x);
    access(y);
    splay(y);
}
I link(R x,R y) //连边
{
    makeroot(x);
    if(findroot(y)!=x)
        f[x]=y;
}
I cut(R x,R y) //断边
{
    makeroot(x);
    if(findroot(y)==x&&f[y]==x&&!c[y][0])
    {
        f[y]=c[x][1]=0;
        pushup(x);
    }
}
int main()
{
    R n=in();
    for(R i=1; i<=n; ++i)
    {
        va[i] = in();//系数
        go[i] = (i+va[i]<=n)?(i+va[i]):n+1;
        link(i,go[i]);
        v[i] = 1;
    }
    R m = in();
    while(m--)
    {
        R type=in(),x=in();
        x++;
        switch(type)
        {
        case 1:
            split(x,n+1);
            printf("%d\n",s[n+1]);
            break;
        case 2:
            R y = in();
            cut(x,go[x]);
            splay(x);//先把x转上去再改,不然会影响Splay信息的正确性
            va[x]=y;
            go[x] = (x+va[x]<=n)?(x+va[x]):n+1;
            link(x,go[x]);
        }
    }
    return 0;
}

https://www.cnblogs.com/flashhu/p/8349984.html

这个链接是flashu大佬的,他说修改边的时候,根本不需要makeroot(x)

因为 我们并不需要查询或者更改指定路径(x−y)(x−y)的信息。

我们只是连边删边,并不改变这条边的信息,所以不需要makeroot

嗯,又学到一个

int main()
{
    register char ch;
    R n,m,j,k;
    in(n);
    for(j=1;j<=n;++j){
        s[j]=1;
        in(k);
        if(j+k<=n)f[j]=j+k;//如果弹飞了就不连边
    }
    in(m);
    while(m--){
        gc;
        if(ch&1){
            in(j);++j;
            access(j);splay(j);//直接查询
            printf("%d\n",s[j]);
        }
        else{
            in(j);in(k);++j;
            access(j);splay(j);
            c[j][0]=f[c[j][0]]=0;//直接断边
            if(j+k<=n)f[j]=j+k;//直接连边
            pushup(j);
        }
    }
    return 0;
}

 

标签:ch,int,弹飞,char,绵羊,getchar,define
来源: https://blog.csdn.net/qq_43439240/article/details/94359709