bzoj 2631 tree

2631: tree

Time Limit: 30 Sec   Memory Limit: 128 MB
Submit: 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,q
接下来n-1行每行两个正整数u,v,描述这棵树
接下来q行,每行描述一个操作

Output

  对于每个/对应的答案输出一行

Sample Input

3 2
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


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);  
    }  
     }    
}     





你可能感兴趣的:(bzoj 2631 tree)