bzoj4034: [HAOI2015]T2

题目

  http://www.lydsy.com/JudgeOnline/problem.php?id=4034

题解

  今天上午NOIP模拟赛死机了两次,结果爆炸120分,被高一的ftm大牛和hzw大牛150碾压了,感觉很不爽,于是来怒刷水题。

  树链剖分+线段树。

代码

//树链剖分
#include <cstdio>
#include <algorithm>
#define ll long long
#define maxn 200010
using namespace std;
ll N, M, head[maxn], to[maxn], tmp[maxn], next[maxn], val[maxn], tid[maxn], fa[maxn],
	top[maxn], size[maxn], son[maxn], deep[maxn], tot, tim, segtot, l[maxn], r[maxn];
void adde(ll a, ll b){to[++tot]=b;next[tot]=head[a];head[a]=tot;}
struct segtree
{
	ll l, r, sum, tag;
	segtree *lch, *rch;
}node[maxn], *root;
void pushdown(segtree *p)
{
	if(p->tag)
	{
		p->sum+=(p->r-p->l+1)*p->tag;
		if(p->lch)p->lch->tag+=p->tag,p->rch->tag+=p->tag;
		p->tag=0;
	}
}
void update(segtree *p)
{
	if(p->lch==0)return;
	pushdown(p->lch),pushdown(p->rch);
	p->sum=p->lch->sum+p->rch->sum;
}
void build(segtree *p, ll l, ll r)
{
	ll mid=(l+r)>>1;
	p->l=l,p->r=r;
	if(l==r){p->sum=val[l];return;}
	build(p->lch=node+ ++segtot,l,mid);
	build(p->rch=node+ ++segtot,mid+1,r);
	p->sum=p->lch->sum+p->rch->sum;
}
ll segsum(segtree *p, ll l, ll r)
{
	pushdown(p);
	ll mid=(p->l+p->r)>>1, ans=0;
	if(l<=p->l and r>=p->r)return p->sum;
	if(l<=mid)ans+=segsum(p->lch,l,r);
	if(r>mid)ans+=segsum(p->rch,l,r);
	return ans;
}
void segtag(segtree *p, ll l, ll r, ll tag)
{
	ll mid=(p->l+p->r)>>1;
	if(l<=p->l and r>=p->r){p->tag+=tag;return;}
	if(l<=mid)segtag(p->lch,l,r,tag);
	if(r>mid)segtag(p->rch,l,r,tag);
	update(p);
}
void dfs1(ll pos)
{
	ll p, v;
	size[pos]=1;
	son[pos]=-1;
	for(p=head[pos];p;p=next[p])
	{
		if((v=to[p])==fa[pos])continue;
		fa[v]=pos;
		deep[v]=deep[pos]+1;
		dfs1(v);
		if(son[pos]==-1 or size[v]>size[son[pos]])son[pos]=v;
		size[pos]+=size[v];
	}
}
void dfs2(ll pos, ll tp)
{
	ll p, v;
	tid[pos]=++tim;
	top[pos]=tp;
	l[pos]=tim;
	if(son[pos]!=-1)dfs2(son[pos],tp);
	for(p=head[pos];p;p=next[p])
	{
		if((v=to[p])==fa[pos] or v==son[pos])continue;
		dfs2(v,v);
	}
	r[pos]=tim;
}
ll calcsum(ll x)
{
	ll tx=top[x], ans=0;
	while(tx!=1)
	{
		ans+=segsum(root,tid[tx],tid[x]);
		x=fa[tx];tx=top[x];
	}
	return ans+segsum(root,1,tid[x]);
}
void init()
{
	ll i, a, b;
	scanf("%lld%lld",&N,&M);
	for(i=1;i<=N;i++)scanf("%lld",tmp+i);
	for(i=1;i<N;i++)scanf("%lld%lld",&a,&b),adde(a,b),adde(b,a);
	dfs1(1);
	dfs2(1,1);
	for(i=1;i<=N;i++)val[tid[i]]=tmp[i];
	build(root=node+ ++segtot,1,tim);
}
void solve()
{
	ll i, x, a, type;
	for(i=1;i<=M;i++)
	{
		scanf("%lld%lld",&type,&x);
		if(type==1)scanf("%lld",&a),segtag(root,tid[x],tid[x],a);
		if(type==2)scanf("%lld",&a),segtag(root,l[x],r[x],a);
		if(type==3)printf("%lld\n",calcsum(x));
	}
}
int main()
{
	init();
	solve();
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值