【LCT】BZOJ 2631:tree

BZOJ 2631:tree


Description

一棵n个点的树,每个点的初始权值为1。对于这棵树有q个操作,每个操作为以下四种操作之一:
+ u v c:将u到v的路径上的点的权值都加上自然数c;
- u1 v1 u2 v2:将树中原有的边(u1,v1)删除,加入一条新边(u2,v2),保证操作完之后仍然是一棵树;
* u v c:将u到v的路径上的点的权值都乘上自然数c;
/ u v:询问u到v的路径上的点的权值和,求出答案对于51061的余数。


Input

第一行两个整数n,q
接下来n-1行每行两个正整数u,v,描述这棵树
接下来q行,每行描述一个操作


Output

对于每个/对应的答案输出一行


Sample Input

3 2
1 2
2 3
* 1 3 4
/ 1 1


Sample Output

4


HINT

10%的数据保证,1<=n,q<=2000
另外15%的数据保证,1<=n,q<=5*10^4,没有-操作,并且初始树为一条链
另外35%的数据保证,1<=n,q<=5*10^4,没有-操作
100%的数据保证,1<=n,q<=10^5,0<=c<=10^4


Solution

比较裸的lct+维护splaysum+设tag
tag比较恶心。。调了好久。。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;

#define maxn 1000001
#define mod 51061

struct splay{
    int ch[2],pf,fa,size;
    unsigned int sum,tag,ctag,v;
    bool rev;
}t[maxn];

void update(int poi)
{
    int l=t[poi].ch[0],r=t[poi].ch[1];
    t[poi].sum=(t[l].sum+t[r].sum+t[poi].v)%mod;
    t[poi].size=t[l].size+t[r].size+1;
}

void work1(unsigned int poi,unsigned int ctt)
{
    t[poi].tag=t[poi].tag*ctt%mod;
    t[poi].sum=t[poi].sum*ctt%mod;
    t[poi].v=t[poi].v*ctt%mod;
}

void work2(unsigned int poi,unsigned int ctt)
{
    t[poi].sum=(t[poi].sum+t[poi].size*ctt)%mod;
    t[poi].v=(t[poi].v+ctt)%mod;
}

void pushdown(int poi)
{
    int l=t[poi].ch[0],r=t[poi].ch[1];
    if(t[poi].rev)
    {
        t[t[poi].ch[0]].rev^=1;
        t[t[poi].ch[1]].rev^=1;
        swap(t[poi].ch[0],t[poi].ch[1]);
        t[poi].rev=0;
    }
    if(t[poi].ctag!=1)
    {
        t[l].ctag=t[l].ctag*t[poi].ctag%mod;
        t[r].ctag=t[r].ctag*t[poi].ctag%mod;
        work1(l,t[poi].ctag);
        work1(r,t[poi].ctag);
        t[poi].ctag=1;
    }
    if(t[poi].tag)
    {
        t[l].tag=(t[l].tag+t[poi].tag)%mod;
        t[r].tag=(t[r].tag+t[poi].tag)%mod;
        work2(l,t[poi].tag);
        work2(r,t[poi].tag);
        t[poi].tag=0;
    }
}

inline int in()
{
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-'0',ch=getchar();
    return x;
}

struct edge{
    int to,lst;
}e[maxn*2];

int last[maxn],tot,stack[maxn],top=0,g[maxn];

void add(int u,int v)
{
    e[++tot]=(edge){v,last[u]};last[u]=tot;
    e[++tot]=(edge){u,last[v]};last[v]=tot;
}

void bfs(int poi,int lst)
{
    g[poi]=1;
    t[poi].size=1;
    if(lst)t[poi].pf=lst;
    for(int i=last[poi];i;i=e[i].lst)
        if(!g[e[i].to])
            bfs(e[i].to,poi);
}

void rotate(int &x,int d)
{
    int y=t[x].fa,z=t[y].fa;t[x].pf=0;
    if(z){if(t[z].ch[0]==y)t[z].ch[0]=x;else t[z].ch[1]=x;}
    else t[x].pf=t[y].pf;
    t[y].pf=0;t[t[x].ch[d]].pf=0;
    t[y].fa=x,t[t[x].ch[d]].fa=y,t[x].fa=z;
    t[y].ch[d^1]=t[x].ch[d];t[x].ch[d]=y;
    update(y);update(x);
}

void splay(int &x)
{
    int y,z;
    for(int i=x;i;i=t[i].fa)stack[++top]=i;
    for(;top;top--)pushdown(stack[top]);
    while(t[x].fa)
    {
        y=t[x].fa;z=t[y].fa;
        if(z)
        {
            if(t[z].ch[0]==y&&t[y].ch[0]==x)rotate(y,1);
            else if(t[z].ch[1]==y&&t[y].ch[1]==x)rotate(y,0);
            else if(t[z].ch[1]==y&&t[y].ch[0]==x)rotate(x,1);
            else if(t[z].ch[0]==y&&t[y].ch[1]==x)rotate(x,0);
        }
        if(t[t[x].fa].ch[0]==x)rotate(x,1);
        else rotate(x,0);
    }
}

void access(int x)
{
    splay(x);
    t[t[x].ch[1]].pf=x,t[t[x].ch[1]].fa=0;t[x].ch[1]=0;
    update(x);
    while(t[x].pf)
    {
        int y=t[x].pf;
        splay(y);
        t[t[y].ch[1]].pf=y,t[t[y].ch[1]].fa=0;
        t[x].pf=0;t[x].fa=y;t[y].ch[1]=x;
        update(y);x=y;
    }
}

void beroot(int x)
{
    access(x);
    splay(x);
    t[x].rev^=1;
}

void cut(int y,int x)
{
    beroot(x);access(y);
    splay(y);t[y].ch[0]=0;
    t[x].fa=t[x].pf=0;
    update(y);
}

void link(int y,int x)
{
    beroot(x),access(y);
    t[x].fa=y;
}

int main()
{
    freopen("2631.in","r",stdin);
    freopen("2631.out","w",stdout);
    int n,m;
    n=in(),m=in();
    for(int i=1;i<n;i++)
    {
        int u=in(),v=in();
        add(u,v);
    }
    for(int i=1;i<=n;i++)t[i].size=t[i].v=t[i].ctag=1;
    bfs(1,0);
    for(int i=1;i<=m;i++)
    {
        char ch;
        int u,v,c,C;
        scanf("\n%c",&ch);
        if(ch=='*')
        {
            v=in(),u=in(),c=in();
            beroot(u);access(v);
            splay(v);
            t[v].ctag=t[v].ctag*c%mod;
            t[v].v=t[v].v*c%mod;
            t[v].sum=t[v].sum*c%mod;
            t[v].tag=t[v].tag*c%mod;
        }
        else if(ch=='-')
        {
            u=in(),v=in();c=in();C=in();
            cut(u,v);link(c,C);
        }
        else if(ch=='+')
        {
            v=in(),u=in(),c=in();
            beroot(u);access(v);
            splay(v);t[v].tag=(c+t[u].tag)%mod;
            t[v].sum=(t[v].sum+t[v].size*c)%mod;
            t[v].v=(t[v].v+c)%mod;
        }
        else
        {
            u=in(),v=in();
            beroot(u);
            access(v);
            splay(v);
            printf("%d\n",t[v].sum%mod);
        }
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值