[BZOJ2631]tree(LCT)

题目:

我是超链接

题解:

也算是LCT的模板题啦,只是这个乘和加的操作先后顺序要搞好
//记得很久以前做过一个线段树的维护乘和加

首先在传递的时候要把要传递下去的+乘上×(相当于这一层的+已经×好了)
那么遇到乘和加在同一级我们选择先×再+
这样的话传下去的时候就是deltac×然后deltaj+(传到下面的时候×就是×,+的话就是先×再把上一层的+加上)
说起来好方啊,大家还是自行参悟代码。。

震惊,3个字符的差异竟带来10s的代码快慢差异,真相竟是!
是。。。。把long long改成unsigned int。。。

代码:

#include <cstdio>
#include <iostream>
#define LL unsigned int
using namespace std;
const int N=100005;
const int Mod=51061;
int ch[N][2],f[N],deltaz[N],size[N],stack[N];
LL key[N],delta[N],sum[N],deltac[N];
int get(int x){return ch[f[x]][1]==x;}
void updata(int x)
{
    sum[x]=(key[x]+(sum[ch[x][0]]+sum[ch[x][1]])%Mod)%Mod;
    size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
}
void mark(int x,int y,int z)
{
    key[x]=(key[x]*z%Mod+y)%Mod;
    sum[x]=(sum[x]*z%Mod+y*size[x]%Mod)%Mod;
    deltac[x]=deltac[x]*z%Mod;
    delta[x]=(delta[x]*z%Mod+y)%Mod;
}
void pushdown(int x)
{
    if (deltaz[x])
    {
        swap(ch[x][0],ch[x][1]);
        deltaz[ch[x][0]]^=1; deltaz[ch[x][1]]^=1;
        deltaz[x]=0;
    }
    mark(ch[x][0],delta[x],deltac[x]);
    mark(ch[x][1],delta[x],deltac[x]);
    deltac[x]=1; delta[x]=0;
}
bool Is_root(int x){return ch[f[x]][0]!=x && ch[f[x]][1]!=x;}
void rotate(int x)
{
    int old=f[x],oldf=f[old],which=get(x);bool gen=Is_root(old);
    ch[old][which]=ch[x][which^1]; f[ch[x][which^1]]=old;
    f[old]=x; ch[x][which^1]=old;
    f[x]=oldf; if (!gen) ch[oldf][ch[oldf][1]==old]=x;
    updata(old);
    updata(x);
}
void splay(int x)
{
    int top=0,i;
    for (i=x;!Is_root(i);i=f[i]) stack[++top]=i;
    stack[++top]=i;
    for (i=top;i>=1;i--) pushdown(stack[i]);

    for (;!Is_root(x);rotate(x))
      if (!Is_root(f[x])) rotate(get(f[x])==get(x)?f[x]:x);
}
void access(int x)
{
    int t=0;
    for (;x;t=x,x=f[x])
    {
        splay(x);
        ch[x][1]=t;
        updata(x);
    }
}
void reverse(int x)
{
    access(x);
    splay(x);
    deltaz[x]^=1;
}
void Link(int x,int y)
{
    reverse(x);
    f[x]=y;
    splay(x);
}
void Cut(int x,int y)
{
    reverse(x);
    access(y); splay(y);
    ch[y][0]=f[x]=0;
}
int qurry(int x,int y)
{
    reverse(x);
    access(y);
    splay(y);
    return sum[y];
}
int main()
{
    int n,q,i;
    scanf("%d%d",&n,&q);
    for (i=1;i<=n;i++) key[i]=deltac[i]=size[i]=1;
    for (i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        Link(x,y);
    }
    while (q--)
    {
        char st[5];int u,v,c,u1,v1,u2,v2;
        scanf("%s",st);
        if (st[0]=='+')
        {
            scanf("%d%d%d",&u,&v,&c);
            reverse(u); access(v);
            splay(v);
            mark(v,c,1);
        }
        else if (st[0]=='-') 
        {
            scanf("%d%d%d%d",&u1,&v1,&u2,&v2);
            Cut(u1,v1); Link(u2,v2);
        }
        else if (st[0]=='*')
        {
            scanf("%d%d%d",&u,&v,&c);
            reverse(u); access(v);
            splay(v);
            mark(v,0,c);
        }
        else 
        {
            scanf("%d%d",&u,&v);
            printf("%d\n",qurry(u,v));
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值