bzoj 2631 tree

来源:互联网 发布:软件架构 pdf 编辑:程序博客网 时间:2024/06/06 05:56

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





0 0
原创粉丝点击