首先如果没做过维护序列这个题的可以先去做一下,对标记有个初步理解,题解在这里。
看到了一道LCT练手题,于是果断刷了,这个题主要恶心在传标记上,别的都是基本的LCT操作。
那么我就说一下传标吧。链接一个博客
这里面讲的不错,下面我只说说我的体会。
我习惯把所有的标记在一个过程里下传,下传顺序一般是旋转,覆盖,加减,注意如果有覆盖的话,连同加减都要一起清掉。然后我下传一般只写在splay的开头,就是先上溯找到根,然后递归地把标记一个一个传下来,事实证明这样做和在旋转的时候一个一个down下来是一样的。
然后是标记法则,这个很重要!一定要规定一个严格的标记法则,就是什么优先,什么影响什么,自己一定要想清楚,否则标记会乱掉。我的标记法则是打标记的同时结算标记,就是在一个点带上标记的时候,这个点的所有域值都是准确值。对于+和×标记来说,把每个数表示成ax+b的形式,然后先降乘再降加,乘标记既要影响乘标记,又要影响加标记,同时还要修改所有维护的域值(如sum,max,key)。这样思路就很清晰了,操作的时候还是老套路,access(x)->lca=access(y)->splay(x)->if (x==lca) else......
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了也至少比一分没有强)