[bzoj3153] sone1 - toptree

传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=3153

AC这道超级工业题留念。花了我整整一个下午写代码+一个上午调代码……

题目大意:维护一棵有点权的有根树,支持:linkcut,换根,对子树/链的赋值/加/询问sum/min/max。

一看12个操作就知道是大毒瘤题。

话说前几天我们某场校内模拟赛有位学长直接搬了这题原题上去,在我们一通痛骂毒瘤之后无意中看到了这么一句话:

"对于100%的数据,都满足n,m<=1e5。为了防止某些询问输出答案过大,这里保证所有输入的值的绝对值都不超过1e3。要注意是绝对值,因为权值可能为负数。"

额……所有输入?也就是说,n,m<=1e3?喵喵喵?!

好吧原来这是良(凉)心送分(命)题(即使暴力也没有那么容易打对好吗亲)(话说这真的不是愚人节题吗)。

吐槽结束进入正题。

这题可以说是toptree的经典题,也有人倾向于叫它AAA树。

以下我以杜教的思路为基础尽可能讲明白这是个啥玩意,如果实在看不懂的话……出门右转杜教题解吧。

前置技能:lct。不会的话出门右转问度娘。

我们需要对lct进行改进来支持我们维护子树信息。

我之前做过一些没有子树修改只有维护子树信息的题(如bjoi2014大融合,https://www.luogu.org/problemnew/show/P4219),不过这题更麻烦之处就在于子树修改。

为了支持子树修改,我们需要对每个点另外维护所有的轻子树,以便支持打标记之类的操作。

同时我们还需要支持access操作中插入和删除一个轻子树的操作。

一个暴力的想法是对每个点直接建一个轻儿子的链表,不过这样会被菊花图卡掉。

于是我们想到了可以再用一个splay来维护。

每个节点有1个father和4个son(记做c[0...3]),其中c[0]和c[1]是重链splay的,c[2]和c[3]是轻子树的。

对于一棵轻子树,我们优先考虑把它直接挂在c[2]或c[3]位置。

如果挂不开了呢?这时一个骚操作来了:我们通过新建虚节点的方式来“扩充位置”。

比如我们把一个虚节点接在c[2],另一个接在c[3],再在两个虚节点的c[2]和c[3]上挂轻子树,就能容下4个轻子树了。类似地我们就能容得下更多的轻子树。注意这些虚点和底下的轻儿子本身也构成一个splay,也是可以旋转的。

于是我们已经可以往一个点的轻子树内插入节点了。每次插入时在最左侧新建一个虚点,把最左侧原来的叶子接到虚点的c[2],把虚点接到原来那个叶子的位置,再把新插入的点接到虚点的c[3],最后把虚点转上去。

那么删除呢?也不难,只要把它父亲那个虚点删掉,把它原来的兄弟接到父亲原来的位置即可。

这样我们就可以支持access操作了。

makeroot操作与普通lct一样。

link就是先把一个点makeroot再插进另一个点的轻子树里。

cut稍有不同,我们采用的是把父节点access,再把子节点从父亲的轻子树里删掉。

再说一下怎么维护信息。

每个节点分别维护实子树和虚子树的信息,其中实子树的信息就是像经典lct那样维护就行,虚子树的信息不光要算它自己的虚子树,还要把它实子树内所有点的虚子树信息也算上。再记一个all表示实子树和虚子树信息之和。

shi[x] = shi[c[0]] + shi[c[1]] + val[x]

xu[x] = all[c[2]] + all[c[3]] + xu[c[0]] + xu[c[1]]

标记也要分实标记和虚标记。其中实标记只沿着实边传递,而虚标记不仅要传向虚子树,同时也会沿着实边传递,不过不同之处在于,传向虚子树时,虚标记不仅会更新子节点的虚标记,同时也会更新它的实标记,而传向实子树只会更新虚标记。

sbj[x] -> sbj[c[0]],sbj[c[1]]

xbj[x] -> xbj[c[0]],xbj[c[1]],sbj[c[2]],sbj[c[3]],xbj[c[2]],xbj[c[3]]

于是我们就可以来做这道题了。子树操作直接将一个点access再splay,然后在它的轻子树(不要在它自身)上打虚标记。链操作就是makeroot(x),access(y),splay(x),然后直接在x上打实标记即可。

不过我们注意到在链操作时要换根,而这棵树是有根树,因此操作完别忘了对原来的根再makeroot回来。

有人或许会有疑问,在这里的换根操作不会导致子树关系变化导致子树标记传递出错吗?据我所知是不会的,大概是因为一次操作只会影响到一条链上的子树关系,而这条链在操作之前就会把标记pushdown掉,就不存在影响了。

终于到了上代码的时间!参考杜教代码折腾了一天总算搞出了这份“还看得过去”的代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define gc getchar()
#define pc putchar
il int in(){
	int x = 0,y = 0,c = gc;
	while(!isdigit(c)) y = c,c = gc;
	while(isdigit(c)) x = (x << 1) + (x << 3) + (c ^ '0'),c = gc;
	return y == '-' ? -x : x;
}
il void out(int q){
	if(q < 0) pc('-'),q = -q;
	if(q >= 10) out(q / 10);
	pc(q % 10 + '0');
}
int n,m,rt,val[100010];
struct ed{
	int f,t;
}e[200010];
struct xx{
	int mx,mn,sm,sz;
	xx(int _mx = 0,int _mn = 0,int _sm = 0,int _sz = 0){
		mx = _mx;mn = _mn;sm = _sm;sz = _sz;
	}
};
struct bj{
	int fz,ad;
	bj(int _mul = 1,int _add = 0){
		fz = _mul;ad = _add;
	}
	bool emp(){return fz == 1 && ad == 0;}
};
xx operator + (xx u,bj v){
	return !u.sz ? u : xx(u.mx * v.fz + v.ad,u.mn * v.fz + v.ad,u.sm * v.fz + v.ad * u.sz,u.sz);
}
xx operator + (xx u,xx v){
	return xx(max(u.mx,v.mx),min(u.mn,v.mn),u.sm + v.sm,u.sz + v.sz);
}
bj operator + (bj u,bj v){
	return bj(u.fz * v.fz,u.ad * v.fz + v.ad);
}
#define l(x) t[x].c[0]
#define r(x) t[x].c[1]
#define xl(x) t[x].c[2]
#define xr(x) t[x].c[3]
#define f(x) t[x].f
#define s(x,y) t[x].c[y]
#define ll(x) t[x].c[tp]
#define rr(x) t[x].c[tp + 1]
struct node{
	int f,c[4],val;
	bj sbj,xbj;
	xx shi,xu,all;
	bool rev,isx;
}t[2000010];
int cnt,ft,bin[2000010];
il void rv(int q){
	t[q].rev ^= 1;swap(l(q),r(q));
}
il void bjshi(int q,bj w){
	t[q].sbj = t[q].sbj + w;t[q].shi = t[q].shi + w;t[q].val = t[q].val * w.fz + w.ad;
	t[q].all = t[q].shi + t[q].xu;
}
il void bjxu(int q,bj w,bool fg = 1){
	t[q].xbj = t[q].xbj + w;t[q].all = t[q].all + w;t[q].xu = t[q].xu + w;
	if(fg) bjshi(q,w);
}
il void ud(int q){
	t[q].shi = t[q].xu = t[q].all = xx(-(1 << 30),1 << 30,0,0);
	if(!t[q].isx) t[q].all = t[q].shi = xx(t[q].val,t[q].val,t[q].val,1);
	for(int i = 0;i < 2;++i) if(s(q,i)) t[q].shi = t[q].shi + t[s(q,i)].shi,t[q].xu = t[q].xu + t[s(q,i)].xu;
	for(int i = 0;i < 4;++i) if(s(q,i)) t[q].all = t[q].all + t[s(q,i)].all;
	for(int i = 2;i < 4;++i) if(s(q,i)) t[q].xu = t[q].xu + t[s(q,i)].all;
}
il void ps(int q){
	if(t[q].rev){
		if(l(q)) rv(l(q));
		if(r(q)) rv(r(q));
		t[q].rev = 0;
	}
	if(!t[q].xbj.emp()){
		for(int i = 0;i < 4;++i) if(s(q,i)) bjxu(s(q,i),t[q].xbj,i >= 2);
		t[q].xbj = bj(1,0);
	}
	if(!t[q].sbj.emp()){
		for(int i = 0;i < 2;++i) if(s(q,i)) bjshi(s(q,i),t[q].sbj);
		t[q].sbj = bj(1,0);
	}
}
il int son(int q,int x){
	if(s(q,x)) ps(s(q,x));
	return s(q,x);
}
il int fd(int q){
	for(int i = 0;i < 4;++i) if(s(f(q),i) == q) return i;
	return -1;
}
il void sets(int q,int w,int tp){
	if(w) f(w) = q;
	s(q,tp) = w;
}
il bool is(int q,int tp = 0){
	if(!tp) return !f(q) || (l(f(q)) != q && r(f(q)) != q);
	return !f(q) || !t[q].isx || !t[f(q)].isx;
}
void ro(int q,int tp){
	if(is(q,tp)) return;
	int p = f(q);
	if(f(p)) sets(f(p),q,fd(p));
	else f(q) = 0;
	f(p) = q;
	if(ll(p) == q){
		ll(p) = rr(q);
		rr(q) = p;
		if(ll(p)) f(ll(p)) = p;
	}
	else{
		rr(p) = ll(q);
		ll(q) = p;
		if(rr(p)) f(rr(p)) = p;
	}
	ud(p);ud(q);
}
il void gx(int q){
	if(f(q)) gx(f(q));
	ps(q); 
}
void sp(int q,int tp = 0){
	ud(q);
	while(!is(q,tp)){
		int p = f(q);
		if(is(p,tp)) ro(q,tp);
		else{
			if((ll(f(p)) == p) ^ (ll(p) == q)) ro(q,tp);
			else ro(p,tp);
			ro(q,tp);
		}
	}
}
il int newd(){
	int q = ft ? bin[ft--] : ++cnt;
	for(int i = 0;i < 4;++i) s(q,i) = 0;f(q) = 0;
	t[q].sbj = t[q].xbj = bj(1,0);
	t[q].shi = t[q].xu = t[q].all = xx(-(1 << 30),1 << 30,0,0);
	t[q].isx = 1;t[q].rev = t[q].val = 0;
	return q;
}
il void deld(int q){bin[++ft] = q;}
void ad(int q,int w){
	for(int i = 2;i < 4;++i) if(!s(w,i)){
		sets(w,q,i);return;
	}
	int p = newd(),u;
	for(u = w;t[xl(u)].isx;u = son(u,2));
	sets(p,xl(u),2);sets(p,q,3);sets(u,p,2);sp(p,2);
}
void del(int q){
	if(t[f(q)].isx){
		sets(f(f(q)),s(f(q),5 - fd(q)),fd(f(q)));
		deld(f(q));sp(f(f(q)),2);
	}
	else sets(f(q),0,fd(q));
	f(q) = 0;
}
void ac(int q){
	int u;
	gx(q);sp(q);
	if(r(q)){
		u = r(q);r(q) = 0;ad(u,q);ud(q);
	} 
	while(f(q)){
		for(u = f(q);t[u].isx;u = f(u));
		sp(u);
		if(r(u)){
			sets(f(q),r(u),fd(q));sp(f(q),2);
		}
		else del(q);
		sets(u,q,1);
		ud(u);q = u;
	}
}
il void mk(int q){
	ac(q);sp(q);rv(q);
}
il int getf(int q){
	ac(q);sp(q);q = son(q,0);
	while(q && r(q)) q = son(q,1);
	return q;
}
il int getr(int q){
	while(f(q)) q = f(q);
	return q;
}
int ct(int q){
	int p = getf(q);
	if(p){
		ac(p);sp(p);del(q);ud(p);
	}
	return p;
}
void lk(int q,int w){
	int p = ct(q);	
	if(getr(q) != getr(w)) p = w;
	if(p){
		ac(p);sp(p);ad(q,p);ud(p);
	}
}
int main(){
	int i,j,op,u,v,w;
	n = in();m = in();rt = 1;
	for(i = 1;i < n;++i){
		e[i].f = in();e[i].t = in();
	}
	for(i = 1;i <= n;++i){
		t[++cnt].val = in();ud(cnt);
	}
	for(i = 1;i < n;++i){
		mk(e[i].f);mk(e[i].t);lk(e[i].f,e[i].t);
	}
	rt = in();mk(rt);
	for(i = 1;i <= m;++i){
		op = in();u = in();
		if(op == 1){
			mk(u);rt = u;
		}
		else if(op == 9){
			v = in();
			lk(u,v);
		}
		else if(op == 0 || op == 3 || op == 4 || op == 5 || op == 11){
			ac(u);sp(u);
			if(op == 3 || op == 4 || op == 11){
				int as = t[u].val;
				for(j = 2;j < 4;++j) if(s(u,j)){
					xx p = t[s(u,j)].all;
					if(op == 3) as = min(as,p.mn);
					else if(op == 4) as = max(as,p.mx);
					else as += p.sm;
				}
				out(as);pc('\n');
			}
			else{
				w = in();
				bj p = bj(op == 5,w);
				t[u].val = t[u].val * p.fz + p.ad;
				for(int j = 2;j < 4;++j) if(s(u,j)) bjxu(s(u,j),p);
				ud(u);
			}
		}
		else{
			v = in();
			mk(u);ac(v);sp(u);
			if(op == 7 || op == 8 || op == 10){
				xx as = t[u].shi;
				out(op == 7 ? as.mn : op == 8 ? as.mx : as.sm);pc('\n'); 
			}
			else{
				w = in();
				bjshi(u,bj(op == 6,w));
			}
			mk(rt);
		}
	}
	return 0;
}

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值