【link-cut tree】



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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章