被春宵的题逼着写link-cut tree了,昨天orz了一两个小时lzn的代码之后,鼓起勇气开写了。
代码写的比较快(毕竟参考了别人的),然后就是一个晚上无尽的debug,画图把草稿本都用光了= _ =, 最后转啊转啊终于转清了。
WC 2006 tube 水管局长
题意略;
树上的问题涉及边权比涉及点权要麻烦,一般的处理方法是每个点附带上他的父亲与他的边,这样写肯定烦透了,所以在cwx的介绍下,把边化成点,
即对于边<u,v>, 新建w点,把<u,v> 拆成 <u,w>和 <w,v>, w点带上<u,v>的边权,这样边权成功的化成了点权^_^
对于询问倒着处理,就完全变成了link-cut tree的基本操作:删边,加边,维护树上路径最大点权。
link-cut tree 的基本思路也很简单,可以说是动态树链剖分,静态的树链剖分是按子树大小来划分轻重边的,但是link-cut tree 对某某点操作之后,把这个点往上延伸的边全部标记为重边,再把这个这个点原来往下延伸的重边去掉(如果有的话),这样居然就神奇的达到了每次操作logn的复杂度,当然,这样的修改就不是线段树能够操作的了,必须使用splay树维护每条链, 可以想见常熟会有多么的大。
然后使劲写,使劲debug就行了。
ps:如果要查询一条链的总体最大权,可以随便选链上的一点询问,但是一定要把它splay到根上,一开始忘记splay了,然后无限WA。
# include <cstdlib> # include <cstdio> # include <cmath> # include <cstring> using namespace std; const int oo = 1073741819, maxn = 100000, maxm = 420000; int size, val[maxn], sum[maxn], fr[maxn], lks[maxn][2], spin[maxn]; int n,m,q, Ex[maxm], Ey[maxm], Ew[maxm], Qx[maxm], Qy[maxm], Qk[maxm]; int top, linke[maxm], next[maxm], point_x[maxm], point_y[maxm], point_id[maxm]; bool occ[maxm]; int ufs[maxn], rep[maxm], ans[maxm]; void swap(int &x, int &y) {int tmp=x; x=y; y=tmp;}; int find(int x) {return x==ufs[x]?x: ufs[x] = find(ufs[x]);}; int max(int x, int y){ return val[x] > val[y] ? x:y; } void update(int x) { sum[x] = max(max(sum[lks[x][0]], sum[lks[x][1]]), x); } int rel(int x) { return (lks[fr[x]][0]== x) + ((lks[fr[x]][1] == x)<< 1) - 1; } int root(int x) { int now; now = rel(x) >= 0 ? root(fr[x]): x; if (spin[x]) { spin[lks[x][0]]^= !!lks[x][0]; spin[lks[x][1]]^= !!lks[x][1]; spin[x]= 0; swap(lks[x][0], lks[x][1]); } return now; } void rotate(int x, int p) { int q, y = fr[x]; lks[y][p] = lks[x][!p]; fr[lks[x][!p]] = lks[y][p]?y:0; if ((q = rel(y)) >= 0) lks[fr[y]][q] = x; fr[x] = fr[y]; lks[x][!p] = y; fr[y] = x; update(y); update(x); } void splay(int x) { int p, q, y, backup = fr[root(x)]; for (;(p = rel(x))!=-1;) { y = fr[x]; if ((q = rel(y)) == -1) rotate(x, p); else if (p == q) rotate(y, q), rotate(x, p); else rotate(x, p), rotate(x, q); } fr[x] = backup; } int access(int u) { int v; for (v=0; u; v=u, u=fr[u]) splay(u),lks[u][1]= v,update(u); return v; } void cut(int x) { access(x); splay(x); fr[lks[x][0]] = 0; lks[x][0] = 0; val[x] = sum[x] = 0; update(x); } void connect(int x, int y, int w)//link { access(y), splay(y),spin[y] ^= 1; val[++size] = w; sum[size] = size; fr[size] = x; fr[y] = size; update(x); } int hash_ask(int x, int y, int w = 0) { int ke,hs = (x*103+y*13)%100007; for (ke = linke[hs]; ke; ke = next[ke]) if (point_x[ke]==x&& point_y[ke]==y) return point_id[ke]; ++top; next[top]=linke[hs];linke[hs]=top;point_x[top]=x;point_y[top]=y; point_id[top] = w; } int query(int x, int y) { access(x); int lca = access(y); if (lca== x) return splay(x), max(x, sum[lks[x][1]]); else return splay(x), max(max(lca, sum[lks[lca][1]]), sum[x]); } int cmp(const void *i, const void *j) { int p = *(int *)i, q = *(int *)j; return Ew[p] - Ew[q]; } void read() { int i, fx, fy; scanf("%d%d%d", &n, &m, &q); size = n; for (i = 1; i <= m; i++) { scanf("%d%d%d", &Ex[i], &Ey[i], &Ew[i]); if (Ex[i]>Ey[i]) swap(Ex[i], Ey[i]); hash_ask(Ex[i], Ey[i], i); } for (i = 1; i <= q; i++) { scanf("%d%d%d", &Qk[i], &Qx[i], &Qy[i]); if (Qx[i]>Qy[i]) swap(Qx[i], Qy[i]); if (Qk[i]==2) occ[hash_ask(Qx[i], Qy[i])] = 1; } for (i = 1; i <= n; i++) ufs[i] = i; for (i = 1; i <= m; i++) rep[i] = i; qsort(rep+1, m, sizeof(rep[1]), cmp); for (i = 1; i <= m; i++) if (!occ[rep[i]]) { fx = find(Ex[rep[i]]); fy = find(Ey[rep[i]]); if (fx!= fy) { ufs[fx] = fy; connect(Ex[rep[i]], Ey[rep[i]], Ew[rep[i]]); } } } int main() { int i, weak; freopen("tube.in", "r", stdin); freopen("tube.out", "w", stdout); read(); val[0] = -oo; for (i = q; i >= 1; i--) if (Qk[i]==1) ans[i] = val[query(Qx[i], Qy[i])]; else { weak = query(Qx[i], Qy[i]); if (val[weak] > Ew[hash_ask(Qx[i], Qy[i])]) { cut(weak); connect(Qx[i], Qy[i], Ew[hash_ask(Qx[i], Qy[i])]); } } for (i = 1; i <= q; i++) if (Qk[i]==1) printf("%d\n", ans[i]); return 0; }
hnoi2010 弹飞绵羊
曾经用块状数组水过的题,现在重新用link-cut tree写一遍。
思路仍然很简单,如果从x可以弹到y,那么连一条x--->y的边,被弹飞则连向一个总的root,那么询问操作变成了询问x点的深度,修改操作变成了把某个子树cut下来,link到另一个点上。没有标记,相当好写,只比块状数组长了10行左右, 1A
不过仍然有一点小插曲,一开始写cut(X)就是简单的把它的父亲access一下,这样是错误的,因为此时的father(x)不一定原树中x的father,必须谨慎的access(x),在splay(x),才能找到x的父亲。写link-cut tree 一定不要混淆splay的父亲和原树父亲。
# include <cstdlib> # include <cstdio> # include <cmath> # include <cstring> using namespace std; const int maxn = 200000+100; int lks[maxn][2], fr[maxn], sum[maxn], res[maxn]; int n, m; inline void update(int x) {sum[x] = 1+sum[lks[x][0]]+sum[lks[x][1]];}; inline int rel(int x) {return (lks[fr[x]][0]==x) + ((lks[fr[x]][1]==x)<<1) - 1;}; void rotate(int x, int p) { int y = fr[x], q; lks[y][p] = lks[x][!p]; fr[lks[y][p]] = lks[y][p]?y:0; if ((q = rel(y))!= -1) lks[fr[y]][q] = x; fr[x] = fr[y]; lks[x][!p] = y; fr[y] = x; update(y); } void splay(int x) { for (int y,p,q;(p = rel(x))!= -1;) { y = fr[x]; if ((q = rel(y)) == -1) rotate(x, p); else if (p == q) rotate(y, q), rotate(x, p); else rotate(x, p), rotate(x, q); } update(x); } void access(int u) { for (int v=0;u;v = u,u= fr[u]) { splay(u); lks[u][1] = v; update(u); } } int query(int u) { access(u);splay(u); return sum[u]-1; } void cut(int u) { access(u); splay(u); fr[lks[u][0]] = 0; lks[u][0] = 0; update(u); } int main() { int i, id, x, y; freopen("bounce.in", "r", stdin); freopen("bounce.out", "w", stdout); scanf("%d", &n);sum[n+1] = 1; for (i = 1; i <= n; i++) scanf("%d", res+i), sum[i] = 1; for (i = 1; i <= n; i++) if (i + res[i] <= n) fr[i] = i+res[i]; else fr[i] = n+1; scanf("%d", &m); for (i = 1; i <= m; i++) { scanf("%d", &id); if (id == 1) scanf("%d", &x), printf("%d\n", query(++x)); else { scanf("%d%d", &x, &y);x++; cut(x); fr[x] = x+y>n?n+1:x+y; } } return 0; }
接下来是两个怎么都AC不了的程序
spoj qtree1:
动态维护树上路径最大边。
Mars的数据水了点,毫无压力的AC,但是spoj上顽固的tle中。。。。。。
# include <cstdlib> # include <cstdio> # include <cmath> # include <cstring> using namespace std; const int maxn = 22000; int spin[maxn], fr[maxn], lks[maxn][2], val[maxn], sum[maxn]; int Ex[maxn], Ey[maxn], Ew[maxn]; int n, size, test; void origin() { memset(val, 130, sizeof(val)); memset(spin,0,sizeof(spin)); memset(fr,0,sizeof(fr)); memset(lks,0,sizeof(lks)); memset(sum,0,sizeof(sum)); } inline int max(int x, int y) { return val[x] > val[y] ?x:y; } inline void update(int x) { sum[x] = max(x, max(sum[lks[x][0]], sum[lks[x][1]])); } inline void swap(int &x, int &y) { int tmp = x; x = y; y = tmp; } inline int rel(int x) { return (lks[fr[x]][0] == x) + ((lks[fr[x]][1] == x) << 1) - 1; } inline int root(int x) { int now = rel(x)==-1? x: root(fr[x]); return now; } inline void rotate(int x, int p) { int q, y = fr[x]; lks[y][p] = lks[x][!p]; fr[lks[y][p]] = lks[y][p]?y:0; if ((q = rel(y))!= -1) lks[fr[y]][q] = x; fr[x] = fr[y]; lks[x][!p] = y; fr[y] = x; update(y); update(x); } inline void splay(int x) { int y, backup = fr[root(x)], p, q; while ((p = rel(x))!= -1) { y = fr[x]; if ((q = rel(y)) == -1) rotate(x, p); else if (p == q) { rotate(y, q); rotate(x, p); } else { rotate(x, p); rotate(x, q); } } fr[x] = backup; } /*void splay(int x) { int backup = fr[root(x)], p; while ((p = rel(x))!= -1) rotate(x, p); fr[x] = backup; }*/ inline int access(int u) { int v; for (v=0; u; v=u, u=fr[u]) { splay(u); lks[u][1] = v; update(u); } return v; } inline void connect(int x, int y, int w) { access(y); splay(y); ++size; fr[y] = size; fr[size] = x; val[size] = w; sum[size] = size; } inline int query(int x, int y) { access(x); int lca = access(y); splay(x); if (x == lca) return max(x, sum[lks[x][1]]); else return max(lca, max(sum[x], sum[lks[lca][1]])); } int main() { int i, x , y; char s[20]; freopen("qtree1.in", "r", stdin); freopen("qtree1.out", "w", stdout); scanf("%d", &test); while (test--) { scanf("%d", &n); size = n; origin(); for (i = 1; i < n; i++) scanf("%d%d%d\n", &Ex[i], &Ey[i], &Ew[i]), connect(Ex[i],Ey[i],Ew[i]); while (1) { scanf("%s", s+1); if (s[1]=='Q') scanf("%d%d", &x, &y),printf("%d\n", x==y?0:val[query(x, y)]); else if (s[1] == 'C') { scanf("%d%d", &x, &y); access(x+n); splay(x+n); val[x+n] = y; update(x+n); } else if (s[1] == 'D') break; } } return 0; }
更新: 在lyp 和tw 的帮助下,加了若干常数优化,砍掉了N多冗余操作,加上了树链剖分初始化,勉强在spoj上AC了。
# include <cstdlib> # include <cstdio> # include <cmath> # include <cstring> using namespace std; const int maxn = 11000; int fr[maxn], lks[maxn][2], val[maxn], sum[maxn]; int ep[maxn], que[maxn], size[maxn]; int n, test; int top, linke[maxn], wis[maxn*2], point[maxn*2], next[maxn*2]; int step[maxn]; void origin() { memset(val, 130, sizeof(val)); memset(fr,0,sizeof(fr)); memset(lks,0,sizeof(lks)); memset(sum,130,sizeof(sum)); top = 0; memset(linke,0,sizeof(linke)); } inline int max(int x, int y) { return x > y ?x:y; } inline void link(int x, int y, int w){++top; next[top]=linke[x];linke[x]=top;point[top]=y;wis[top]=w;} inline void update(int x) { sum[x] = max(val[x], max(sum[lks[x][0]], sum[lks[x][1]])); } inline int rel(int x) { return lks[fr[x]][1] == x ? 1 : lks[fr[x]][0] == x ? 0 : -1;//(lks[fr[x]][0] == x) + ((lks[fr[x]][1] == x) << 1) - 1; } void rotate(int x, int p) { int q, y = fr[x]; lks[y][p] = lks[x][!p]; fr[lks[y][p]] = lks[y][p]?y:0; if ((q = rel(y))!= -1) lks[fr[y]][q] = x; fr[x] = fr[y]; fr[y] = x; lks[x][!p] = y; update(y); } void splay(int x) { int y, p, q; while ((p = rel(x))!= -1) { y = fr[x]; if ((q = rel(y)) == -1) rotate(x, p); else if (p == q) rotate(y, q),rotate(x, p); else rotate(x, p),rotate(x, q); }; update(x); } int access(int u) { int v; for (v=0; u; v=u, u=fr[u]) { splay(u); lks[u][1] = v; update(u); } return v; } inline void connect(int x, int y, int w) { lks[x][1] = y; update(x); } inline int query(int x, int y) { access(x); int lca = access(y); if (x == lca) return sum[lks[x][1]]; else return splay(x),max(sum[x], sum[lks[lca][1]]); } void dfs(int u, int fa) { int bj = 0, ke; size[u] = 1; for (ke = linke[u]; ke; ke = next[ke]) if (point[ke]!= fa) { ep[ke+1>>1] = point[ke]; fr[point[ke]] = u; val[point[ke]] = sum[point[ke]] = wis[ke]; dfs(point[ke], u); size[u] += size[point[ke]]; if (size[point[ke]] > size[point[bj]]) bj = ke; } if (bj) connect(u, point[bj], wis[bj]); } int main() { int i, x,y,w; char s[20]; freopen("qtree.in", "r", stdin); freopen("qtree.out", "w", stdout); scanf("%d", &test);test++; while (--test>0) { scanf("%d", &n); size[0] = 0; origin(); for (i = 1; i < n; i++) scanf("%d%d%d\n", &x, &y, &w), link(x, y, w), link(y, x, w); dfs(1, 0); while (1) { scanf("%s", s+1); if (s[1]=='Q') scanf("%d%d", &x, &y),printf("%d\n", x==y?0:query(x, y)); else if (s[1] == 'C') { scanf("%d%d", &x, &y); splay(ep[x]); val[ep[x]] = y; update(ep[x]); } else if (s[1] == 'D') break; } } return 0; }
毫无缩行。。。。。。
zjoj2008 count bzoj1036动态维护两点间最大点权和权值和。
和上面几乎一模一样,写起来没什么压力。
bzoj上迅速的WA,然后自己对拍一直没拍死。。。。。。。>_< >_<
先留着,同样毫无缩行:
# include <cstdlib> # include <cstdio> # include <cmath> # include <cstring> using namespace std; const int maxn = 100000; int tot[maxn], spin[maxn], fr[maxn], lks[maxn][2], val[maxn], sum[maxn]; int Ex[maxn], Ey[maxn], Ew[maxn], d[maxn]; int n, q, size; int max(int x, int y) { return val[x] > val[y] ?x:y; } void update(int x) { if (!x) return; sum[x] = max(x, max(sum[lks[x][0]], sum[lks[x][1]])); tot[x] = val[x] + tot[lks[x][1]] + tot[lks[x][0]]; } void swap(int &x, int &y) { int tmp = x; x = y; y = tmp; } int rel(int x) { return (lks[fr[x]][0] == x) + ((lks[fr[x]][1] == x) << 1) - 1; } int root(int x) { int now = rel(x)==-1? x: root(fr[x]); } void rotate(int x, int p) { int q, y = fr[x]; lks[y][p] = lks[x][!p]; fr[lks[y][p]] = lks[y][p]?y:0; if ((q = rel(y))!= -1) lks[fr[y]][q] = x; fr[x] = fr[y]; lks[x][!p] = y; fr[y] = x; update(y); update(x); } void splay(int x) { int y, backup = fr[root(x)], p, q; while ((p = rel(x))!= -1) { y = fr[x]; if ((q = rel(y)) == -1) rotate(x, p); else if (p == q) { rotate(y, q); rotate(x, p); } else { rotate(x, p); rotate(x, q); } } fr[x] = backup; } int access(int u) { int v; for (v=0; u; v=u, u=fr[u]) { splay(u); lks[u][1] = v; update(u); } return v; } void connect(int x, int y) { access(y); splay(y); fr[y] = x; val[x] = d[x]; val[y] = d[y]; sum[x] = x; sum[y] = y; update(y); update(x); } int query_max(int x, int y) { access(x); int lca = access(y); splay(x); if (x == lca) return max(x, sum[lks[x][1]]); else return max(lca, max(sum[x], sum[lks[lca][1]])); } int query_tot(int x, int y) { access(x); int lca = access(y); splay(x); if (x == lca) return val[x] + tot[lks[x][1]]; else return val[lca] + tot[x] + tot[lks[lca][1]]; } int main() { int i, test, x , y; char s[20]; freopen("1036.in", "r", stdin); freopen("1036.out", "w", stdout); scanf("%d", &n); val[0] = -1073741819; for (i = 1; i < n; i++) scanf("%d%d%\n", &Ex[i], &Ey[i]); for (i = 1; i <= n; i++) scanf("%d", &d[i]); for (i = 1; i < n; i++) connect(Ex[i], Ey[i]); scanf("%d", &q); for (i = 1; i <= q; i++) { scanf("%s", s+1); scanf("%d%d\n", &x, &y); if (s[2]=='M') printf("%d\n", val[query_max(x, y)]); else if (s[2] == 'S') printf("%d\n", query_tot(x, y)); else { access(x); splay(x); val[x] = y; update(x); } } return 0; }