tree bzoj-2631

题目大意:给定一个n个点的树,每个点的初始权值为1,支持:删边加边(这两个操作同时进行,保证操作之后还是一棵树),路径加,路径乘,查询路径和。

注释:$1\le n,q\le 10^5$,$1\le c \le 10^4$。

想法:暴力给的很优雅:15‘没有加边删边且保证是链,我们直接用线段树维护。50'没有加边删边,我们裸上树剖+线段树。满分的话我们用LCT维护加边删边。

具体地:我的习惯是先×后+,所以每一次乘法都先将加法标记也进行修改。询问的话直接makeroot,我们节点信息直接维护原树子树信息,然后对应的查询就是链上的信息。

最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100050
#define mod 51061
#define ls ch[p][0]
#define rs ch[p][1]
#define get(x) (ch[f[x]][1]==x)
typedef unsigned int ui;
int n,m,ch[N][2],f[N],rev[N];
ui sum[N],mul[N],add[N],siz[N],val[N];
char opt[10];
inline bool isrt(int x)
{
    return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;
}
void pushdown(int p)
{
    if(mul[p]!=1)
	{
        ui u=mul[p];
        sum[ls]=sum[ls]*u%mod; mul[ls]=mul[ls]*u%mod; add[ls]=add[ls]*u%mod; val[ls]=val[ls]*u%mod;
        sum[rs]=sum[rs]*u%mod; mul[rs]=mul[rs]*u%mod; add[rs]=add[rs]*u%mod; val[rs]=val[rs]*u%mod;
        mul[p]=1;
    }
    if(add[p])
	{
        ui d=add[p];
        sum[ls]=(sum[ls]+siz[ls]*d%mod)%mod; add[ls]=(add[ls]+d)%mod; val[ls]=(val[ls]+d)%mod;
        sum[rs]=(sum[rs]+siz[rs]*d%mod)%mod; add[rs]=(add[rs]+d)%mod; val[rs]=(val[rs]+d)%mod;
        add[p]=0;
    }
    if(rev[p])
	{
        swap(ch[ls][0],ch[ls][1]);
        swap(ch[rs][0],ch[rs][1]);
        rev[ls]^=1; rev[rs]^=1;
        rev[p]=0;
    }
}
void pushup(int p)
{
    siz[p]=siz[ls]+siz[rs]+1;
    sum[p]=(sum[ls]+sum[rs]+val[p])%mod;
}
void update(int p)
{
    if(!isrt(p)) update(f[p]);
    pushdown(p);
}
void rotate(int x)
{
    int y=f[x],z=f[y],k=get(x);
    if(!isrt(y)) ch[z][ch[z][1]==y]=x;
    ch[y][k]=ch[x][!k]; f[ch[y][k]]=y;
    ch[x][!k]=y; f[y]=x; f[x]=z;
    pushup(y); pushup(x);
}
void splay(int x)
{
    update(x);
    for(int fa;fa=f[x],!isrt(x);rotate(x))
        if(!isrt(fa))
            rotate(get(fa)==get(x)?fa:x);
}
void access(int p)
{
    int t=0;
    while(p) splay(p),rs=t,pushup(p),t=p,p=f[p];
}
void makeroot(int p)
{
    access(p); splay(p); swap(ls,rs); rev[p]^=1;
}
void link(int x,int p)
{
    makeroot(x); f[x]=p;
}
void cut(int x,int p)
{
    makeroot(x); access(p); splay(p); ls=f[x]=0;
}
void split(int x,int p)
{
    makeroot(x); access(p); splay(p);
}
int main()
{
    scanf("%d%d",&n,&m);
    int i,x,y,z,w;
    for(i=1;i<=n;i++) val[i]=mul[i]=siz[i]=sum[i]=1;
    for(i=1;i<n;i++)
	{
        scanf("%d%d",&x,&y); link(x,y);
    }
    for(i=1;i<=m;i++)
	{
        scanf("%s%d%d",opt,&x,&y);
        int p=y;
        if(opt[0]=='+')
		{
            scanf("%d",&z); split(x,p);
            sum[p]=(sum[p]+siz[p]*z%mod)%mod;
            val[p]=(val[p]+z)%mod;
            add[p]=(add[p]+z)%mod;
        }
		else if(opt[0]=='-')
		{
            scanf("%d%d",&z,&w); cut(x,y); link(z,w);
        }
		else if(opt[0]=='*')
		{
            scanf("%d",&z); split(x,p);
            sum[p]=sum[p]*z%mod;
            val[p]=val[p]*z%mod;
            add[p]=add[p]*z%mod;
            mul[p]=mul[p]*z%mod;
        }
		else
		{
            split(x,p); printf("%u\n",sum[p]);
        }
    }
}

 小结:LCT是个好东西。