一棵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的余数。
一棵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的余数。
数据规模和约定
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,但是在下放以及存储标记时特别容易出错。一开始我的想法是不让加法和乘法标记同时存在,即如果当前点有乘法或加法标记就让标记下放,直到不能下放,然后再记录当前标记,但是这样做的话每次会更新一些没有用的节点,多出了很多不必要的运算,所有TLE了,而且在下放时特别容易出错,有可能下放不及时,或更新不及时,所有我放弃了自己的想法。
看了看hzwer的博客,发现他的两个标记是可以共存的,用两个数组分别记录加乘标记,而且每次更新加法标记时还需要乘上当前点的乘法标记 ,因为如果当前点需要进行(x+a)*m ,那么相当于是x*m+a*m ,这样就不能只是加上需要加的数了,而是需要用乘法标记更新加法标记。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define N 100003 #define ll unsigned int //刚开始直接用的int,WA了,看黄学长的博客,说需要用unsigned int改完就AC了,但是个人认为如果参与运算的数中有int 的话,在不加强制转换的情况下,还是有可能出错 using namespace std; int n,m; int size[N],ch[N][3],fa[N],rev[N],top,st[N]; ll at[N],mt[N],sum[N],key[N]; const int p=51061; int isroot(int x) { return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x; } int get(int x) { return ch[fa[x]][1]==x; } void update(int x) { if (!x) return; size[x]=(size[ch[x][0]]+size[ch[x][1]]+1)%p; sum[x]=(sum[ch[x][0]]+sum[ch[x][1]]+key[x])%p; } void cal(int x,ll m,ll a)//mt[x]存储的是乘号标记,at[x]存储的是加号标记 { if (!x) return ; key[x]=(key[x]*m+a)%p; sum[x]=(sum[x]*m+a*size[x])%p; mt[x]=(mt[x]*m)%p; at[x]=(at[x]*m+a)%p; } void pushdown(int x) { if (!x) return; if (rev[x])//下放翻转标记 { rev[ch[x][0]]^=1; rev[ch[x][1]]^=1; rev[x]=0; swap(ch[x][0],ch[x][1]); } if(mt[x]!=1||at[x]!=0) { cal(ch[x][0],mt[x],at[x]);cal(ch[x][1],mt[x],at[x]); } mt[x]=1;at[x]=0; } void rotate(int x) { int y=fa[x]; int z=fa[y]; int which=get(x); if (!isroot(y)) ch[z][ch[z][1]==y]=x; ch[y][which]=ch[x][which^1]; fa[ch[y][which]]=y; ch[x][which^1]=y; fa[y]=x; fa[x]=z; update(y); update(x); } void splay(int x) { top=0; st[++top]=x; for (int i=x;!isroot(i);i=fa[i]) st[++top]=fa[i]; for (int i=top;i>=1;i--) pushdown(st[i]); while(!isroot(x)) { int y=fa[x]; if (!isroot(y)) rotate(get(x)==get(y)?y:x); rotate(x); } } void access(int x) { int t=0; while(x) { splay(x); ch[x][1]=t; update(x); t=x; x=fa[x]; } } void rever(int x) { access(x); splay(x); rev[x]^=1; } void link(int x,int y) { rever(x); fa[x]=y; } void cut(int x,int y) { rever(x); access(y); splay(y); ch[y][0]=fa[x]=0; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) key[i]=mt[i]=size[i]=sum[i]=1,rev[i]=0; for (int i=1;i<=n-1;i++) { int x,y; scanf("%d%d\n",&x,&y); link(x,y); } for (int i=1;i<=m;i++) { char c=getchar(); if (c=='-') { int x,y,z,k; scanf("%d%d%d%d\n",&x,&y,&z,&k); cut(x,y); link(z,k); } else if (c=='+') { int x,y,z; scanf("%d%d%d\n",&x,&y,&z); rever(y); access(x); splay(x); cal(x,1,z); } else if (c=='*') { int x,y,z; scanf("%d%d%d\n",&x,&y,&z); rever(y); access(x); splay(x); cal(x,z,0); } else { int x,y; scanf("%d%d\n",&x,&y); rever(y); access(x); splay(x); printf("%d\n",sum[x]%p); } } }