【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;
}


你可能感兴趣的:(优化,tree,query,Access,include,2010)