[bzoj2631]tree【LCT】
【题目描述】
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行,每行描述一个操作
接下来n-1行每行两个正整数u,v,描述这棵树
接下来q行,每行描述一个操作
Output
对于每个/对应的答案输出一行
Sample Input
3 2
1 2
2 3
* 1 3 4
/ 1 1
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
LCT模板题,讲一下LCT基本操作(对lct一无所知的请看https://www.cnblogs.com/BLADEVIL/p/3510997.html)
1.access(x):LCT核心操作,将x到root的所有边设为偏爱边,断开其他与这条莲相邻的偏爱边
2.makeroot(x) :将x变为根
3.getroad(u,v) :(我YY的)将u设为根并将v转到u所在splay的根上(进行access操作),此时v没有右子树,左子树即要查询的链。
4.link(u,v):添加u与v的边
5.cut(u,v):删去u与v的边
6.isroot(x):判断x是否为当前splay的根,如果是,那么fa[x]的左右儿子都因没有x
实现见代码:
/* -------------- user Vanisher problem bzoj-2631 LCT ----------------*/ # include <bits/stdc++.h> # define ll long long # define ui unsigned int # define N 100010 # define P 51061 using namespace std; ui read(){ ui tmp=0, fh=1; char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();} while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();} return tmp*fh; } struct lct{ ui pl,pr,size,tag,tag0,tag1,sum,num,fa; }T[N]; ui st[N],n,q; void pushtag(ui x){ if (x==0) return; if (T[x].tag==1){ T[T[x].pl].tag^=1; T[T[x].pr].tag^=1; T[x].tag^=1; swap(T[x].pl,T[x].pr); } if (T[x].tag1!=1){ ui num=T[x].tag1; T[T[x].pl].tag0=(T[T[x].pl].tag0*num)%P; T[T[x].pl].tag1=(T[T[x].pl].tag1*num)%P; T[T[x].pr].tag0=(T[T[x].pr].tag0*num)%P; T[T[x].pr].tag1=(T[T[x].pr].tag1*num)%P; T[x].num=(T[x].num*T[x].tag1)%P; T[x].sum=(T[x].sum*T[x].tag1)%P; T[x].tag1=1; } if (T[x].tag0!=0){ ui num=T[x].tag0; T[T[x].pl].tag0=(T[T[x].pl].tag0+num)%P; T[T[x].pr].tag0=(T[T[x].pr].tag0+num)%P; T[x].sum=(T[x].sum+T[x].tag0*T[x].size)%P; T[x].num=(T[x].num+T[x].tag0)%P; T[x].tag0=0; } } void change(ui x){ pushtag(T[x].pl); pushtag(T[x].pr); T[x].sum=(T[x].num+T[T[x].pl].sum+T[T[x].pr].sum)%P; T[x].size=1+T[T[x].pl].size+T[T[x].pr].size; } bool isroot(ui x){ return T[T[x].fa].pl!=x&&T[T[x].fa].pr!=x; } void zig(ui x){ ui y=T[x].fa; if (!isroot(y)){ if (T[T[y].fa].pl==y) T[T[y].fa].pl=x; else T[T[y].fa].pr=x; } T[x].fa=T[y].fa; T[y].pl=T[x].pr; T[T[x].pr].fa=y; T[y].fa=x; T[x].pr=y; change(y); change(x); } void zag(ui x){ ui y=T[x].fa; if (!isroot(y)){ if (T[T[y].fa].pl==y) T[T[y].fa].pl=x; else T[T[y].fa].pr=x; } T[x].fa=T[y].fa; T[y].pr=T[x].pl; T[T[x].pl].fa=y; T[y].fa=x; T[x].pl=y; change(y); change(x); } void splay(ui x){ ui y=x,top=0; st[++top]=x; while (!isroot(y)) st[++top]=y=T[y].fa; for (ui i=top; i>=1; i--) pushtag(st[i]); while (!isroot(x)){ y=T[x].fa; if (isroot(y)) if (T[y].pl==x) zig(x); else zag(x); else if (T[T[y].fa].pl==y) if (T[y].pl==x) zig(y), zig(x); else zag(x), zig(x); else if (T[y].pl==x) zig(x), zag(x); else zag(y), zag(x); } } void access(ui u){ ui v=0; while (u){ splay(u); T[u].pr=v; T[v].fa=u; change(u); v=u; u=T[u].fa; } } void makeroot(ui u){ access(u); splay(u); T[u].tag^=1; } void getroad(ui u, ui v){ makeroot(u); access(v); splay(v); } void link(ui u, ui v){ makeroot(u); T[u].fa=v; } void cut(ui u, ui v){ getroad(u,v); T[v].pl=0; T[u].fa=0; change(v); } int main(){ n=read(); q=read(); for (ui i=1; i<=n; i++) T[i].size=1,T[i].tag1=1,T[i].num=T[i].sum=1; for (ui i=1; i<n; i++) link(read(),read()); char opt; ui u,v,c; for (ui i=1; i<=q; i++){ scanf("\n%c",&opt); if (opt=='+'){ u=read(), v=read(); c=read(); getroad(u,v); T[v].tag0=(T[v].tag0+c)%P; } if (opt=='-'){ u=read(), v=read(); cut(u,v); u=read(), v=read(); link(u,v); } if (opt=='*'){ u=read(), v=read(); c=read(); getroad(u,v); T[v].tag1=(T[v].tag1*c)%P; T[v].tag0=(T[v].tag0*c)%P; } if (opt=='/'){ u=read(), v=read(); getroad(u,v); pushtag(v); printf("%d\n",T[v].sum); } } return 0; }
tips:此题要用unsigned int,跑的挺快的。