国家集训队 Tree II 题解

题目传送门

题目大意: 给一棵树,若干种操作:路径加,路径乘,删边加边,求路径和。

题解

看到删边加边就知道是 L C T LCT LCT 了。

路径加路径乘都是打个标记的事,但是在更新 s u m sum sum 的时候, s u m sum sum 不仅要乘上乘法标记,还要加上加法标记乘 s i z e size size 的积。( s i z e size size 是子树大小)

以及这题的模数的平方刚好爆 i n t int int,所以要开 l o n g   l o n g long~long long long

以及我长时间 WA 5分的原因可能是比较稀有的……

众所周知, l a z y lazy lazy 标记有两种写法,第一种在得到 l a z y lazy lazy 标记时更新自己的 v a l u e value value 以及 s u m sum sum,然后在 p u s h d o w n pushdown pushdown 的时候更新儿子的 v a l u e value value s u m sum sum;第二种是得到 l a z y lazy lazy 标记的时候不更新,在 p u s h d o w n pushdown pushdown 的时候更新自己的 v a l u e value value s u m sum sum

对于线段树而言,这两种写法是等价的,但是对于 S p l a y Splay Splay 而言,第二种写法却是错的。因为 r o t a t e rotate rotate 操作里面,旋转完之后父亲变成了我的儿子,要 u p d a t e ( f a ) update(fa) update(fa) 然后 u p d a t e ( x ) update(x) update(x),如果用第二种写法的话就会错,原因想想就能明白。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100010
#define mod 51061
#define ll long long

struct node{
	ll val,sum,lazyplus,lazytimes;
	int size,lazy;
	node *zuo,*you,*fa;
	node():val(1),sum(1),lazy(0),lazyplus(0),lazytimes(1),zuo(NULL),you(NULL),fa(NULL){}
	void update(ll plus,ll times)
	{
		sum=(sum*times%mod+plus*size%mod)%mod;
		val=(val*times%mod+plus)%mod;
	}
	void pushdown()
	{
		if(lazy||lazyplus||lazytimes!=1)
		{
			if(zuo!=NULL)zuo->lazy^=lazy,zuo->lazyplus=(zuo->lazyplus*lazytimes%mod+lazyplus)%mod,zuo->lazytimes=zuo->lazytimes*lazytimes%mod,zuo->update(lazyplus,lazytimes);
			if(you!=NULL)you->lazy^=lazy,you->lazyplus=(you->lazyplus*lazytimes%mod+lazyplus)%mod,you->lazytimes=you->lazytimes*lazytimes%mod,you->update(lazyplus,lazytimes);
			if(lazy)swap(zuo,you);
			lazy=0;lazyplus=0;lazytimes=1;
		}
	}
	void check()
	{
		size=1,sum=val;
		if(zuo!=NULL)size+=zuo->size,sum=(sum+zuo->sum)%mod;
		if(you!=NULL)size+=you->size,sum=(sum+you->sum)%mod;
	}
	bool notroot(){return fa!=NULL&&(fa->zuo==this||fa->you==this);}
};
node *root[maxn];
node *zhan[maxn];
int t=0;
void rotate(node *x)
{
	node *fa=x->fa,*gfa=fa->fa;
	if(fa->zuo==x)
	{
		fa->zuo=x->you;
		if(x->you!=NULL)x->you->fa=fa;
		x->you=fa;
	}
	else
	{
		fa->you=x->zuo;
		if(x->zuo!=NULL)x->zuo->fa=fa;
		x->zuo=fa;
	}
	fa->fa=x;x->fa=gfa;
	if(gfa!=NULL&&gfa->zuo==fa)gfa->zuo=x;
	if(gfa!=NULL&&gfa->you==fa)gfa->you=x;
	fa->check();x->check();
}
#define witch(x) (x->fa->zuo==x)
void splay(node *x)
{
	node *now=x;
	zhan[++t]=now;
	while(now->notroot())zhan[++t]=now=now->fa;
	while(t)zhan[t--]->pushdown();
	while(x->notroot())
	{
		if(x->fa->notroot()&&witch(x)==witch(x->fa))rotate(x->fa),rotate(x);
		else rotate(x);
	}
}
void access(node *x)
{
	for(node *y=NULL;x!=NULL;y=x,x=x->fa)
	splay(x),x->you=y,x->check();
}
void makeroot(node *x)
{
	access(x);splay(x);
	x->lazy^=1;x->pushdown();
}
void split(node *x,node *y)
{
	makeroot(x);access(y);
	splay(x);
}
void link(node *x,node *y)
{
	makeroot(x);
	x->fa=y;
}
void del(node *x,node *y)
{
	split(x,y);
	y->fa=x->you=NULL;
	x->check();
}
int n,m;

int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++)
	root[i]=new node();
	for(int i=1,x,y;i<n;i++)
	scanf("%d %d",&x,&y),link(root[x],root[y]);
	for(int i=1;i<=m;i++)
	{
		char s[3];
		int x,y,xx,yy;
		scanf("%s",s);
		if(s[0]=='+')
		{
			scanf("%d %d %d",&x,&y,&xx);
			split(root[x],root[y]);
			root[x]->lazyplus=(root[x]->lazyplus+(ll)xx)%mod;
			root[x]->update(xx,1);
			root[x]->pushdown();
		}
		if(s[0]=='-')
		{
			scanf("%d %d %d %d",&x,&y,&xx,&yy);
			del(root[x],root[y]);link(root[xx],root[yy]);
		}
		if(s[0]=='*')
		{
			scanf("%d %d %d",&x,&y,&xx);
			split(root[x],root[y]);
			root[x]->lazytimes=root[x]->lazytimes*(ll)xx%mod;
			root[x]->lazyplus=root[x]->lazyplus*(ll)xx%mod;
			root[x]->update(0,xx);
			root[x]->pushdown();
		}
		if(s[0]=='/')
		{
			scanf("%d %d",&x,&y);
			split(root[x],root[y]);
			printf("%lld\n",root[x]->sum);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述: 给定一些文件夹和文件,要求将它们按照层级关系输出成一个树形结构。 输入格式: 输入的第一行包含一个整数 n,表示文件夹和文件的个数。 接下来 n 行,每行包含一个字符串,表示一个文件夹或者文件。 其中,文件名和文件夹名都不包含空格和斜杠,文件名包含一个小数点,表示文件后缀。 输出格式: 输出一个树形结构,每行表示一个文件夹或者文件,按照层级关系缩进,文件夹名称后面要加上一个斜杠。 注意,最后一个文件夹或者文件名称后面不能有空格。 样例: 输入: 6 /root /etc /root/abcd.txt /root/bcd/ /etc/test/ /root/bcd/efg.txt 输出: /root /abcd.txt /bcd/ /efg.txt /etc /test/ 算法1 (模拟) $O(n)$ 思路: 本题需要我们输出文件夹和文件的层级结构,因此可以考虑使用哈希表记录每个文件夹和文件的层级结构。 对于每个文件夹和文件,我们可以通过判断其路径中"/"的数量来确定其所在的层级结构,具体来说,每个"/"表示一层。因此,我们可以将路径按"/"分开,然后统计"/"的数量,就可以得到该文件夹或文件所在的层级结构。 同时,由于本题要求输出树形结构,因此我们需要对每个文件夹和文件进行缩进处理,使其在输出时具有层级关系。具体来说,我们可以通过其所在的层级结构来确定输出时需要添加的缩进空格数量。 最后,我们可以按照文件夹和文件的层级结构从小到大的顺序进行输出,这样能够保证每个文件夹和文件的父节点已经被输出过了。 时间复杂度 哈希表的查询和插入操作都是常数级别的,因此总时间复杂度为 $O(n)$。 C++ 代码 算法2 (模拟) $O(nlogn)$ 思路: 本题可以使用字典树来实现,具体来说,我们可以将每个文件夹和文件的路径看作一个字符串,然后将所有字符串插入到字典树中。 同时,我们可以定义一个结构体,用来存储每个字符串的层级结构和缩进空格数量,具体来说,每个字符串的层级结构可以通过其在字典树中的深度来确定,而每个字符串的缩进空格数量则可以通过其所在的层级结构来确定。 最后,我们可以按照字符串的层级结构从小到大的顺序进行输出,这样能够保证每个字符串的父节点已经被输出过了。 时间复杂度 插入字符串的时间复杂度为 $O(nlogn)$,输出字符串的时间复杂度也为 $O(nlogn)$,因此总时间复杂度为 $O(nlogn)$。 C++ 代码

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值