bzoj2631 tree(伍一鸣)

首先如果没做过维护序列这个题的可以先去做一下,对标记有个初步理解,题解在这里

看到了一道LCT练手题,于是果断刷了,这个题主要恶心在传标记上,别的都是基本的LCT操作。

那么我就说一下传标吧。链接一个博客

这里面讲的不错,下面我只说说我的体会。

我习惯把所有的标记在一个过程里下传,下传顺序一般是旋转,覆盖,加减,注意如果有覆盖的话,连同加减都要一起清掉。然后我下传一般只写在splay的开头,就是先上溯找到根,然后递归地把标记一个一个传下来,事实证明这样做和在旋转的时候一个一个down下来是一样的。

然后是标记法则,这个很重要!一定要规定一个严格的标记法则,就是什么优先,什么影响什么,自己一定要想清楚,否则标记会乱掉。我的标记法则是打标记的同时结算标记,就是在一个点带上标记的时候,这个点的所有域值都是准确值。对于+和×标记来说,把每个数表示成ax+b的形式,然后先降乘再降加,乘标记既要影响乘标记,又要影响加标记,同时还要修改所有维护的域值(如sum,max,key)。这样思路就很清晰了,操作的时候还是老套路,access(x)->lca=access(y)->splay(x)->if (x==lca) else......

 

tree
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<cstring>
  6 #define maxn 220000
  7 #define inf 1000000000
  8 #define ms 51061
  9 using namespace std;
 10 int fir[maxn],c[maxn][2],fa[maxn],size[maxn],rev[maxn];
 11 unsigned int key[maxn],sum[maxn],add[maxn],mul[maxn];
 12 int n,m,tot;
 13 unsigned int ans;
 14 struct et
 15 {
 16     int s,t,next;
 17     void add(int x,int y)
 18     {
 19         s=x; t=y; next=fir[x]; fir[x]=tot;
 20     }
 21 }e[maxn];
 22 
 23 inline void update(int x)
 24 {
 25     if (!x) return ;
 26     size[x]=size[c[x][0]]+size[c[x][1]]+1;
 27     sum[x]=(sum[c[x][0]]+sum[c[x][1]]+key[x])%ms;
 28 }
 29 
 30 inline bool root(int x)
 31 {
 32     return (c[fa[x]][0]!=x&&c[fa[x]][1]!=x);
 33 }
 34 
 35 inline void reverse(int x)
 36 {
 37     if (!x) return ;
 38     swap(c[x][0],c[x][1]);
 39     rev[x]^=1;
 40 }
 41 
 42 inline void mult(int x,int w)
 43 {
 44     if (!x) return ;
 45     key[x]=key[x]*w%ms;
 46     sum[x]=sum[x]*w%ms;
 47     mul[x]=mul[x]*w%ms; 
 48     add[x]=add[x]*w%ms;
 49 }
 50 
 51 inline void addit(int x,int w)
 52 {
 53     if (!x) return ;
 54     key[x]=(key[x]+w)%ms;
 55     sum[x]=(sum[x]+w*size[x])%ms;
 56     add[x]=(add[x]+w)%ms;
 57 }
 58 
 59 inline void down(int x)
 60 {
 61     if (rev[x])
 62     {
 63         reverse(c[x][0]); reverse(c[x][1]);
 64         rev[x]=0;
 65     }
 66     if (mul[x]!=1)
 67     {
 68         mult(c[x][0],mul[x]); mult(c[x][1],mul[x]);
 69         mul[x]=1;
 70     }
 71     if (add[x])
 72     {
 73         addit(c[x][0],add[x]); addit(c[x][1],add[x]);
 74         add[x]=0;
 75     }
 76 }
 77 
 78 inline void zigzag(int x)
 79 {
 80     //down(x);
 81     int y=fa[x],z=fa[y];
 82     int p=(c[y][1]==x),q=p^1;
 83     if (!root(y))
 84         if (c[z][0]==y) c[z][0]=x; else c[z][1]=x;
 85     fa[x]=z; fa[y]=x; fa[c[x][q]]=y;
 86     c[y][p]=c[x][q]; c[x][q]=y;
 87     update(y);
 88 }
 89 
 90 inline void relax(int x)
 91 {
 92     if (!root(x)) relax(fa[x]);
 93     down(x);
 94 }
 95 
 96 inline void splay(int x)
 97 {
 98     relax(x);
 99     while (!root(x))
100     {
101         int y=fa[x],z=fa[y];
102         //if (z) down(z); down(y);
103         if (!root(y))
104             if ((c[y][0]==x)xor(c[z][0]==y)) zigzag(x); else zigzag(y);
105         zigzag(x);
106     }
107     //down(x);
108     update(x);
109 }
110 
111 inline int access(int x)
112 {
113     int y=0;
114     for (;x;x=fa[x])
115     {
116         splay(x);
117         c[x][1]=y;
118         update(x);
119         y=x;
120     }
121     return y;
122 }
123 
124 inline void link(int x,int y)
125 {
126     access(x); splay(x);
127     reverse(x);
128     fa[x]=y;
129 }
130 
131 inline void cut(int x,int y)
132 {
133     access(x); splay(y);
134     if (fa[x]==y) fa[x]=0; 
135     else
136     {
137         access(y); splay(x);
138         fa[y]=0;
139     }
140 }
141 
142 inline void dfs(int now)
143 {
144     for (int j=fir[now];j;j=e[j].next)
145     {
146         int k=e[j].t;
147         if (fa[now]!=k) fa[k]=now,dfs(k);
148     }
149 }
150 
151 int main()
152 {
153     //freopen("tree.in","r",stdin);
154     scanf("%d%d",&n,&m);
155     int x,y;
156     for (int i=1;i<=n;i++) size[i]=key[i]=mul[i]=sum[i]=1,add[i]=0;
157     for (int i=1;i<n;i++)
158     {
159         scanf("%d%d",&x,&y);
160         e[++tot].add(x,y);
161         e[++tot].add(y,x);
162     }
163     int rot=rand()%n+1;
164     dfs(rot);
165     char sign;
166     int u,v,z,lca;
167     for (int i=1;i<=m;i++)
168     {
169         scanf("\n%c",&sign);
170         switch (sign)
171         {
172             case '+':
173                 scanf("%d%d%d",&u,&v,&z);
174                 access(u); lca=access(v); splay(u);
175                 sum[lca]=(sum[lca]+z)%ms,key[lca]=(key[lca]+z)%ms;
176                 addit(c[lca][1],z);
177                 if (u!=lca)    addit(u,z);
178             break;
179             case '-':
180                 scanf("%d%d%d%d",&x,&y,&u,&v);
181                 cut(x,y);
182                 link(u,v);
183             break;
184             case '*':
185                 scanf("%d%d%d",&u,&v,&z);
186                 access(u); lca=access(v); splay(u);
187                 sum[lca]=(sum[lca]+key[lca]*(z-1))%ms,key[lca]=(key[lca]*z)%ms;
188                 mult(c[lca][1],z);
189                 if (u!=lca) mult(u,z);
190             break;
191             case '/':
192                 scanf("%d%d",&u,&v);
193                 access(u); lca=access(v); splay(u);
194                 if (u==lca)
195                     ans=(key[lca]+sum[c[lca][1]])%ms;
196                 else 
197                     ans=(key[lca]+sum[c[lca][1]]+sum[u])%ms;
198                 printf("%d\n",ans);
199             break;
200         }
201     }
202     return 0;
203 }

 

我注释掉的那3处down(x)可以替换掉splay中第一个relax(x)

这个题有个trick,运算过程中会爆longlong,但是long long 会灰常慢,但题目规定数字都是正的,于是可以开unsigned int,这样可以快一倍!(以后这种带乘法的题还是开long long 比较保险,T了也至少比一分没有强)

转载于:https://www.cnblogs.com/zig-zag/archive/2013/04/27/3046200.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值