【解题报告】2020牛客暑期多校集训营第七场C题A National Pandemic——树剖

题目大意:给你一棵树,让你完成以下操作:

<1>输入x,w,对任意结点y的权值加上w-dis(x,y)

<2>输入x,让x的权值取min{F(x),0},其中F(x)为x结点的权值

<3>输入x,输出结点的权值

对于操作2很简单只需要用个delta数组记录,如果当前F(x)>0,则delta[x]-=F(x),反之则不用管

然后来讨论重点的操作1:

首先,w-dis(x,y)=w-dep[x]-dep[y]+2dep[lca(x,y)]

分析这个式子可以发现,对于任意结点来说w-dep[x]相当于是个常数,所以用个变量专门记录;dep[y]相当于每个结点自己的深度;所以每次操作1都会令每个结点的权值加上∑w-dep[x],再减去自身的深度*操作1的次数。(这些不要在树上记录,直接用变量或者数组记录,树上要维护的是lca的深度)

接下来考虑如何计算2dep[lca(x,y)]:对于每次操作1,从根到x这条路径上所有的结点权值+2,然后求2dep[lca(x,y)]就是求根到y这条路径上结点的权值之和,这个就直接变成裸的树剖了。这种操作也可以当做个小技巧吧。

于是结束,注意要初始化son,不然会RE;还有要开long long

AC代码:

#include 
using namespace std;
typedef long long LL;
const int maxn=5e4+10;
struct edge{
	int to,next;
}e[maxn<<1];
int head[maxn],cnt=0;
int n;//节点数目
int mod;//题目中的mod
int d[maxn];//d[i]表示第i个结点的深度
int fa[maxn];//fa[i]表示第i个结点的parent
int son[maxn];//son[i]表示第i个结点的重子节点
int tot[maxn];//tot[i]表示以第i个结点为根节点的子树结点个数
int top[maxn];//top[i]表示第i个结点所在重链的顶部节点
int idx[maxn];//idx[i]表示第i个结点对应的dfs序
int a[maxn];//a[i]表示dfs序为i的结点的初值
int w[maxn];//w[i]表示第i个结点的初值
int ct=0;//辅助记录idx和a,没啥用
int m,r;//m次询问,r为树的根
void add_edge(int u,int v)
{
	cnt++;
	e[cnt].to=v;
	e[cnt].next=head[u];
	head[u]=cnt;
}
struct node{
	int left,right;
	LL lazy;
	LL sum;
	int siz;
}tree[maxn<<2];
void pushup(int root)
{
	tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;
}
void pushdown(int root)
{
	if(!tree[root].lazy) return;
	tree[root<<1].sum=tree[root<<1].sum+tree[root].lazy*(LL)tree[root<<1].siz;
	tree[root<<1|1].sum=tree[root<<1|1].sum+tree[root].lazy*(LL)tree[root<<1|1].siz;
	tree[root<<1].lazy=tree[root<<1].lazy+tree[root].lazy;
	tree[root<<1|1].lazy=tree[root<<1|1].lazy+tree[root].lazy;
	tree[root].lazy=0;
}
void build(int root,int left,int right)
{
	tree[root].left=left,tree[root].right=right;
	tree[root].siz=right-left+1;
	if(left==right){
		tree[root].sum=(LL)a[left];
		return;
	}
	int mid=(left+right)>>1;
	build(root<<1,left,mid);
	build(root<<1|1,mid+1,right);
	pushup(root);
}
void add(int root,int left,int right,int val)
{
	if(left<=tree[root].left&&tree[root].right<=right){
		tree[root].sum=(tree[root].sum+(LL)tree[root].siz*(LL)val);
		tree[root].lazy=(tree[root].lazy+(LL)val);
		return;
	}
	pushdown(root);
	if(tree[root<<1].right>=left) add(root<<1,left,right,val);
	if(tree[root<<1|1].left<=right) add(root<<1|1,left,right,val);
	pushup(root);
}
LL search(int root,int left,int right)
{
	if(left<=tree[root].left&&tree[root].right<=right) return tree[root].sum;
	pushdown(root);
	LL res=0;
	if(tree[root<<1].right>=left) res=(res+search(root<<1,left,right));
	if(tree[root<<1|1].left<=right) res=(res+search(root<<1|1,left,right));
	return res;
}
int dfs1(int now,int pre,int dep)
{
	d[now]=dep;
	fa[now]=pre;
	tot[now]=1;
	int mxson=-1;
	for(int i=head[now];i;i=e[i].next){
		int nxt=e[i].to;
		if(nxt==pre) continue;
		tot[now]+=dfs1(nxt,now,dep+1);
		if(tot[nxt]>mxson) mxson=tot[nxt],son[now]=nxt;
	}
	return tot[now];
}
void dfs2(int now,int topf)
{
	idx[now]=++ct;
	a[ct]=w[now];
	top[now]=topf;
	if(!son[now]) return;
	dfs2(son[now],topf);
	for(int i=head[now];i;i=e[i].next){
		int nxt=e[i].to;
		if(nxt==fa[now]||nxt==son[now]) continue;
		dfs2(nxt,nxt);
	}
}
void tree_add(int x,int y,int val)
{
	while(top[x]!=top[y]){
		if(d[top[x]]d[y]) swap(x,y);
	add(1,idx[x],idx[y],val);
}
LL tree_sum(int x,int y)
{
	LL res=0;
	while(top[x]!=top[y]){
		if(d[top[x]]d[y]) swap(x,y);
	res=(res+search(1,idx[x],idx[y]));
	return res;
}
LL sum=0;
LL num=0;
LL delta[maxn];
void init()
{
	memset(head,0,sizeof(head));
	cnt=ct=0;
	sum=0;
	num=0;
	memset(delta,0,sizeof(delta));
	memset(tree,0,sizeof(tree));
	memset(son,0,sizeof(son));
}
int main()
{
	int t;scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		init();
		for(int i=1;i=0) delta[x]-=fx;
			}
			else if(opt==3){
				scanf("%d",&x);
				LL ans=sum-(LL)d[x]*num+tree_sum(r,x)+delta[x];
				printf("%lld\n",ans);
			}
		}
	}
}
/*
1
5 6
1 2
1 3
2 4
2 5
1 1 5
3 4
2 1
1 2 7
3 3
3 1
*/ 
/*
3
9
6
*/ 

你可能感兴趣的:(个人学习感悟)