【BZOJ2631】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的余数。
+ 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
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
动态树(题解完结)
。。。。。。
好吧,这确实是一颗明显的动态树,但是题解肯定不是这么写的。
对于+和*两种操作,我们需要的是打标记,-操作就是标准的Link和Cut了,以及最后的询问将u设为根,access(v)即可。
这些都不是重点。我们发现,Splay的节点上同时拥有加和乘两种标记,在处理时先后顺序会造成一定的麻烦。这里蒟蒻我介绍一种从其他大佬哪里学来的方式,能够降低思考难度。
可以看到,我们将两个标记同时处理,即不论是*操作还是+操作,我们都维护两个标记。这样就能够避免诸如标记遗漏或者是顺序颠倒的问题了。
注意一下运算的优先级的问题。
。。。。。。
好吧,这确实是一颗明显的动态树,但是题解肯定不是这么写的。
对于+和*两种操作,我们需要的是打标记,-操作就是标准的Link和Cut了,以及最后的询问将u设为根,access(v)即可。
这些都不是重点。我们发现,Splay的节点上同时拥有加和乘两种标记,在处理时先后顺序会造成一定的麻烦。这里蒟蒻我介绍一种从其他大佬哪里学来的方式,能够降低思考难度。
inline void Cal(int v,int mul,int add){if(v==0)return ;
tree[v].val=(tree[v].val*mul+add)%mod;
tree[v].sum=(tree[v].sum*mul+tree[v].size*add)%mod;
tree[v].time=tree[v].time*mul%mod;
tree[v].add=(tree[v].add*mul+add)%mod;
return ;
}
可以看到,我们将两个标记同时处理,即不论是*操作还是+操作,我们都维护两个标记。这样就能够避免诸如标记遗漏或者是顺序颠倒的问题了。
注意一下运算的优先级的问题。
CODE
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int mod=51061;
inline int read(){
char c;int rec=0;
while((c=getchar())<'0'||c>'9');
while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();
return rec;
}
int n,m;
struct LCT_Tree{
long long F,s[2],val,size;
long long add,time,sum,rev;
inline void NewNode(int fa,int x){
F=fa;val=x;s[0]=s[1]=0;add=0;time=1;size=1;sum=val;return ;
}
}tree[100005];
inline bool Isroot(int v){return tree[tree[v].F].s[0]!=v&&tree[tree[v].F].s[1]!=v;}
inline void Pushup(int v){
tree[v].sum=tree[tree[v].s[0]].sum+tree[v].val+tree[tree[v].s[1]].sum;
tree[v].sum%=mod;
tree[v].size=tree[tree[v].s[0]].size+1+tree[tree[v].s[1]].size;
return ;
}
inline void Rotate(int v){
int p=tree[v].F,g=tree[p].F;
int t1=(v==tree[p].s[1]),t2=(p==tree[g].s[1]),S=tree[v].s[!t1];
if(!Isroot(p))tree[g].s[t2]=v;tree[v].F=g;
tree[p].s[t1]=S;tree[S].F=p;
tree[v].s[!t1]=p;tree[p].F=v;
Pushup(p);return ;
}
inline void Cal(int v,int mul,int add){if(v==0)return ;
tree[v].val=(tree[v].val*mul+add)%mod;
tree[v].sum=(tree[v].sum*mul+tree[v].size*add)%mod;
tree[v].time=tree[v].time*mul%mod;
tree[v].add=(tree[v].add*mul+add)%mod;
return ;
}
inline void Rev(int v){if(v==0)return ;
tree[v].rev^=1;swap(tree[v].s[0],tree[v].s[1]);return ;
}
inline void Pushdown(int v){
if(tree[v].rev){Rev(tree[v].s[0]);Rev(tree[v].s[1]);tree[v].rev=0;}
Cal(tree[v].s[0],tree[v].time,tree[v].add);
Cal(tree[v].s[1],tree[v].time,tree[v].add);
tree[v].time=1;tree[v].add=0;
return ;
}
inline void Lazy(int v){if(!Isroot(v))Lazy(tree[v].F);Pushdown(v);return ;}
inline void Splay(int v){
Lazy(v);
while(!Isroot(v)){
int p=tree[v].F,g=tree[p].F;
if(!Isroot(p))(v==tree[p].s[1])^(p==tree[g].s[1])?Rotate(v):Rotate(p);
Rotate(v);
}Pushup(v);return ;
}
inline void Access(int v){
for(int temp=0;v;temp=v,v=tree[v].F)
{Splay(v);tree[v].s[1]=temp;Pushup(v);}
return ;
}
inline void Make_Root(int v){Access(v);Splay(v);Rev(v);return ;}
inline void Link(int v1,int v2){Make_Root(v1);tree[v1].F=v2;return ;}
inline void Cut(int v1,int v2){Make_Root(v1);Access(v2);Splay(v2);tree[v2].s[0]=tree[v1].F=0;Pushup(v1);Pushup(v2);return ;}
inline void Plus(int v1,int v2,int x){Make_Root(v1);Access(v2);Splay(v2);Cal(v2,1,x);return ;}
inline void Times(int v1,int v2,int x){Make_Root(v1);Access(v2);Splay(v2);Cal(v2,x,0);return ;}
inline void Ask(int v1,int v2){Make_Root(v1);Access(v2);Splay(v2);cout<<tree[v2].sum%mod<<'\n';return ;}
int main(){
n=read();m=read();
int x,y,p,q;char c;
for(int i=1;i<=n;i++)tree[i].NewNode(0,1);
for(int i=1;i<n;i++)Link(read(),read());
for(int i=1;i<=m;i++){
while((c=getchar())!='+'&&c!='-'&&c!='*'&&c!='/');
if(c=='+'){
x=read();y=read();p=read();
Plus(x,y,p);
}
if(c=='-'){
x=read();y=read();p=read();q=read();
Cut(x,y);Link(p,q);
}
if(c=='*'){
x=read();y=read();p=read();
Times(x,y,p);
}
if(c=='/'){
x=read();y=read();Ask(x,y);
}
}
return 0;
}
爆int这种事情总是会发生的,还是long long更好。