首页 > 技术文章 > 洛谷P1501 [国家集训队]Tree II(LCT,Splay)

flashhu 2018-01-24 21:20 原文

洛谷题目传送门

关于LCT的其它问题可以参考一下我的LCT总结

一道LCT很好的练习放懒标记技巧的题目。

一开始看到又做加法又做乘法的时候我是有点mengbi的。

然后我想起了模板线段树2。。。。。。(相信各位Dalao一定做过这道题)

这里的维护懒标记方法很像。除了翻转标记以外还要维护乘法标记和加法标记。

根据运算优先级,乘法是要先算的,所以先放,放的时候子树的\(sum\),乘法标记,加法标记,儿子的\(val\)统统都要乘一遍。

放加法标记的时候,想到线段树的区间大小是稳定的,而Splay并不是,所以还要维护\(size\),于是子树的\(sum\)要加上子树的\(size\)再乘上标记,而儿子的\(val\)和加法标记直接加上该标记的值。

再注意一个小细节。

有没有觉得51061这个数好小啊。。。我看到的时候特高兴,不用担心longlong的问题了。然而。。。

\(51061^2=2607225721>2147483647\)

所以要开unsigned int。。。。。。

还是上代码吧

#include<cstdio>
#include<cstdlib>
#define R register unsigned int
#define I inline
#define YL 51061
#define lc c[x][0]
#define rc c[x][1]
#define mul(x) x*=c;x%=YL
#define add(x,c) x+=c;x%=YL
#define G ch=getchar()
#define gc G;while(ch<'*')G
#define in(z) gc;z=ch&15;G;while(ch>'*')z*=10,z+=ch&15,G;
const int N=100009;
unsigned int n,f[N],c[N][2],v[N],s[N],sz[N],lm[N],la[N],st[N];
bool r[N];
I bool nroot(R x){//好像Dalao都写的是isroot
	return c[f[x]][0]==x||c[f[x]][1]==x;
}
I void pushup(R x){
	s[x]=(s[lc]+s[rc]+v[x])%YL;
	sz[x]=sz[lc]+sz[rc]+1;
}
I void pushr(R x){//翻转
	R t=lc;lc=rc;rc=t;r[x]^=1;
}
I void pushm(R x,R c){//乘
	mul(s[x]);mul(v[x]);mul(lm[x]);mul(la[x]);
}
I void pusha(R x,R c){//加
	add(s[x],c*sz[x]);add(v[x],c);add(la[x],c);
}
I void pushdown(R x){
	if(lm[x]!=1)pushm(lc,lm[x]),pushm(rc,lm[x]),lm[x]=1;
	if(la[x])   pusha(lc,la[x]),pusha(rc,la[x]),la[x]=0;
	if(r[x])   {if(lc)pushr(lc);if(rc)pushr(rc);r[x]=0;}
}
I void rotate(R x){
	R y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k];
	if(nroot(y))c[z][c[z][1]==y]=x;c[x][!k]=y;c[y][k]=w;//注意if(nroot(y)),本蒟蒻经常忘写
	if(w)f[w]=y;f[y]=x;f[x]=z;
	pushup(y);
}
I void splay(R x){
	R y=x,z=0;
	st[++z]=y;//手动放个栈
	while(nroot(y))st[++z]=y=f[y];
	while(z)pushdown(st[z--]);
	while(nroot(x)){
		y=f[x];z=f[y];
		if(nroot(y))
			rotate((c[y][0]==x)^(c[z][0]==y)?x:y);
		rotate(x);
	}
	pushup(x);
}
I void access(R x){
	for(R y=0;x;x=f[y=x])
		splay(x),rc=y,pushup(x);
}
I void makeroot(R x){
	access(x);
	splay(x);
	pushr(x);
}
I void split(R x,R y){
	makeroot(x);
	access(y);
	splay(y);
}
I void link(R x,R y){
	makeroot(x);f[x]=y;
}
I void cut(R x,R y){
	split(x,y);f[x]=c[y][0]=0;
}
int main()
{
	register char ch;
	R q,i,a,b,k;
	in(n);in(q);
	for(i=1;i<=n;++i)v[i]=sz[i]=lm[i]=1;//注意乘法标记的初值为1
	for(i=1;i<n;++i){
		in(a);in(b);
		link(a,b);
	}
	while(q--){
		gc;
		switch(ch){
		case '+':
			in(a);in(b);in(k);
			split(a,b);pusha(b,k);
			break;
		case '-':
			in(a);in(b);cut(a,b);
			in(a);in(b);link(a,b);
			break;
		case '*':
			in(a);in(b);in(k);
			split(a,b);pushm(b,k);
			break;
		case '/':
			in(a);in(b);
			split(a,b);
			printf("%d\n",s[b]);
		}
	}
	return 0;
}

推荐阅读