被春宵的題逼着寫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;
}