【解题报告】HDU3966-Aragorn‘s Story——树剖

裸树剖,注意多组数据和初始化的问题就好了。

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=5e4+10;
struct edge{
	int to,next;
}e[maxn<<1];
int head[maxn<<1],cnt=0;
int n;//节点数目
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;
	int lazy;
	int 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*tree[root<<1].siz);
	tree[root<<1|1].sum=(tree[root<<1|1].sum+tree[root].lazy*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=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+tree[root].siz*val);
		tree[root].lazy=(tree[root].lazy+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);
}
int search(int root,int left,int right)
{
	if(left<=tree[root].left&&tree[root].right<=right) return tree[root].sum;
	pushdown(root);
	int 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[top[y]]) swap(x,y);
		add(1,idx[top[x]],idx[x],val);
		x=fa[top[x]];
	}
	if(d[x]>d[y]) swap(x,y);
	add(1,idx[x],idx[y],val);
}
int tree_sum(int x,int y)
{
	int res=0;
	while(top[x]!=top[y]){
		if(d[top[x]]<d[top[y]]) swap(x,y);
		res=(res+search(1,idx[top[x]],idx[x]));
		x=fa[top[x]];
	}
	if(d[x]>d[y]) swap(x,y);
	res=(res+search(1,idx[x],idx[y]));
	return res;
}
void updTree(int x,int val)
{
	add(1,idx[x],idx[x]+tot[x]-1,val);
}
int askTree(int x)
{
	return search(1,idx[x],idx[x]+tot[x]-1);
}
void init()
{
	memset(head,0,sizeof(head));
	memset(son,0,sizeof(son));
	memset(tree,0,sizeof(tree));
	cnt=ct=0;
}
int main()
{
	int M;
	while(~scanf("%d%d%d",&n,&M,&m)){
		init();
		for(int i=1;i<=n;i++) scanf("%d",&w[i]);
		for(int i=1;i<n;i++){
			int u,v;scanf("%d%d",&u,&v);
			add_edge(u,v);
			add_edge(v,u);
		}
		r=1;
		dfs1(r,0,1);
		dfs2(r,r);
		build(1,1,n);
		while(m--){
			char opt[2];scanf("%s",opt);
			int x,y,z;
			int ans;
			if(opt[0]=='I'){
				scanf("%d%d%d",&x,&y,&z);
				tree_add(x,y,z);
			}
			else if(opt[0]=='D'){
				scanf("%d%d%d",&x,&y,&z);
				tree_add(x,y,-z);
			}
			else if(opt[0]=='Q'){
				scanf("%d",&x);
				int ans=search(1,idx[x],idx[x]);
				printf("%d\n",ans);
			}
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值