2631: tree
Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 3145 Solved: 1055
[ Submit][ Status][ Discuss]
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-1行每行两个正整数u,v,描述这棵树
接下来q行,每行描述一个操作
Output
Sample Input
1 2
2 3
* 1 3 4
/ 1 1
Sample Output
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
Source
题解: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);
}
}
}