zoukankan      html  css  js  c++  java
  • 洛谷P1501 [国家集训队]Tree II(LCT,Splay)

    洛谷题目传送门

    关于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
    ",s[b]);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    自主问题--KMP算法
    题解--luogu--CSP2019.S.Day2.T4--Emiya 家今天的饭
    浅谈——RMQ
    浅谈——LCA
    NOIP(si le)或者CSP初赛之——前序中序后序,前缀中缀后缀
    浅说——查分约束
    浅说——tarjan
    C++ RQNOJ 星门龙跃
    C++ 洛谷 1261:【例9.5】城市交通路网
    刷题
  • 原文地址:https://www.cnblogs.com/flashhu/p/8343609.html
Copyright © 2011-2022 走看看