BZOJ 2631 tree LCT

题意:链接

方法: LCT

解析:

挺简单好想的LCT

首先按照题意link出一棵树

接下来有四种操作,第一种是打一个加法标记,直接打就OK,第二种是cut原来的一条边再link出一条新边。

第三种是打一个乘法标记,(和bzoj1798好像)

第四种是求u到v路径的权值和,这只需要把v换成根,之后访问x,把x splay上去,拿出来此时的sum[x]就是答案。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100010
#define mod 51061
using namespace std;
typedef unsigned int uint;
char s[5];
uint x,y;
uint n,q;
uint rt[N];
uint rev[N];
uint mul[N];
uint add[N];
uint sum[N];
uint val[N]; 
uint size[N];
uint ch[N][2];
uint fa[N];
void init()
{
    for(int i=1;i<=n;i++)mul[i]=1,rt[i]=1,size[i]=1,val[i]=1;
}
void pushdown_add_and_mul_operation(int x,int add_opt,int mul_opt)
{
    sum[x]=(sum[x]*mul_opt+size[x]*add_opt)%mod;
    val[x]=(val[x]*mul_opt+add_opt)%mod;
    add[x]=(add[x]*mul_opt+add_opt)%mod;
    mul[x]=(mul[x]*mul_opt)%mod;
} 
void pushdown(uint x)
{
    if(rev[x])
    {
        rev[ch[x][0]]^=1,rev[ch[x][1]]^=1;
        rev[x]^=1;
        swap(ch[x][0],ch[x][1]);
    }
    if(mul[x]!=1||add[x]!=0)
    {
        if(ch[x][0]!=0)
        {
            pushdown_add_and_mul_operation(ch[x][0],add[x],mul[x]);
        }
        if(ch[x][1]!=0)
        {
            pushdown_add_and_mul_operation(ch[x][1],add[x],mul[x]);
        }
        mul[x]=1,add[x]=0;
    }
}
void down(uint x)
{
    if(!rt[x])down(fa[x]);
    pushdown(x);
}
void pushup(uint x)
{
    size[x]=(size[ch[x][0]]+size[ch[x][1]]+1)%mod;
    sum[x]=(sum[ch[x][0]]+sum[ch[x][1]]+val[x])%mod;
}
void rotate(uint x)
{
    uint y=fa[x],kind=ch[y][1]==x;
    ch[y][kind]=ch[x][!kind];
    fa[ch[y][kind]]=y;
    ch[x][!kind]=y;
    fa[x]=fa[y];
    fa[y]=x;
    if(rt[y])rt[y]=0,rt[x]=1;
    else ch[fa[x]][ch[fa[x]][1]==y]=x;
    pushup(y);
}
void splay(uint x)
{
    down(x);
    while(!rt[x])
    {
        uint y=fa[x],z=fa[y];
        if(rt[y])rotate(x);
        else if((ch[y][1]==x)==(ch[z][1]==y))
        {
            rotate(y),rotate(x);
        }else rotate(x),rotate(x);
    }
    pushup(x);
}
void access(uint x)
{
    uint y=0;
    while(x)
    {
        splay(x);
        rt[ch[x][1]]=1,rt[y]=0;
        ch[x][1]=y;
        pushup(x);
        y=x,x=fa[x];
    }
}
void movetoroot(uint x)
{
    access(x);
    splay(x);
    rev[x]=1;
    pushdown(x);
}
void link(uint x,uint y)
{
    movetoroot(x);
    fa[x]=y;
}
void cut(uint x,uint y)
{
    movetoroot(x);
    access(y);
    splay(y);
    rt[x]=1;
    ch[y][0]=0;
    fa[x]=0;
}
int main()
{
    scanf("%u%u",&n,&q);
    init();
    for(int i=1;i<n;i++)
    {
        scanf("%u%u",&x,&y);
        link(x,y);
    }
    for(int i=1;i<=q;i++)
    {
        scanf("%s",s);
        uint x,y,z,w;
        switch(s[0])
        {
            case '+':
                scanf("%u%u%u",&x,&y,&z);
                movetoroot(y);access(x);splay(x);
                pushdown_add_and_mul_operation(x,z,1);
                break;
            case '-':scanf("%u%u%u%u",&x,&y,&z,&w);cut(x,y);link(z,w);break;
            case '*': 
                scanf("%u%u%u",&x,&y,&z);
                movetoroot(y);access(x);splay(x);
                pushdown_add_and_mul_operation(x,0,z);
                break;
            case '/':
                scanf("%u%u",&x,&y);
                movetoroot(y);access(x);splay(x);
                printf("%u\n",sum[x]);
                break;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值