C-A National Pandemic 2020牛客暑期多校训练营(第七场)

https://ac.nowcoder.com/acm/contest/5672/C

比赛的时候又想用对询问分块nsqrt(n)logn做。。。。还好被队友叫住了,被队友用点分树nlogn过了

然而我不会点分树,看了题解发现树链剖分巨简单

我们发现一个1操作对其他的点增加的是w-dep[x]-dep[y]+2*dep[lca],那么可以把w-dep[x]单独拿出来求和sum,对于3操作,每个点的值就是sum-num*dep[y]+2*dep[lca],num为1操作的次数,这个2*dep[lca]可以转换成1操作修改时从x到根节点每个位置+2,然后求y的答案的时候,就是y到根节点的和,这中间就包含了其他1操作的2*dep[lca]对此时对y询问的影响。

那么就是树链剖分建线段树区间加法区间求和了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxl=1e5+10;

int n,m,cnt;ll ans;
int son[maxl],fa[maxl],tot[maxl],top[maxl],dep[maxl];
int idx[maxl],dy[maxl];ll dta[maxl];
vector<int> e[maxl];
struct node
{
	int l,r;ll sum,tag;
}tr[maxl<<2];

inline void dfs1(int u,int f)
{
	tot[u]=1;
	for(int v:e[u])
	{
		if(v==f) continue;
		dep[v]=dep[u]+1;fa[v]=u;
		dfs1(v,u);
		tot[u]+=tot[v];
		if(tot[v]>tot[son[u]])
			son[u]=v;
	}
}

inline void dfs2(int u,int f,int topf)
{
	idx[u]=++cnt;dy[cnt]=u;top[u]=topf;
	if(!son[u]) return;
	dfs2(son[u],u,topf);
	for(int v:e[u])
	{
		if(idx[v]) continue;
		dfs2(v,u,v);
	}
}

inline void build(int k,int l,int r)
{
	tr[k].l=l;tr[k].r=r;tr[k].sum=tr[k].tag=0;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
}

inline void prework()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		e[i].clear();idx[i]=dy[i]=dta[i]=0;
		son[i]=fa[i]=tot[i]=dep[i]=top[i]=0;
	}
	int u,v;
	for(int i=1;i<=n-1;i++)
	{
		scanf("%d%d",&u,&v);
		e[u].push_back(v);
		e[v].push_back(u);
	}
	cnt=0;dep[1]=1;
	dfs1(1,0);
	dfs2(1,0,1);
	build(1,1,n);
}

inline void gank(int k)
{
	ll d=tr[k].tag;
	if(tr[k].l!=tr[k].r)
	{
		tr[k<<1].tag+=d;
		tr[k<<1].sum+=(tr[k<<1].r-tr[k<<1].l+1)*d;
		tr[k<<1|1].tag+=d;
		tr[k<<1|1].sum+=(tr[k<<1|1].r-tr[k<<1|1].l+1)*d;
	}
	tr[k].tag=0;
}

inline void add(int k,int l,int r,ll x)
{
	if(tr[k].tag)
		gank(k);
	tr[k].sum+=(r-l+1)*x;
	if(tr[k].l==l && tr[k].r==r)
	{
		tr[k].tag+=x;
		return;
	}
	int mid=(tr[k].l+tr[k].r)>>1;
	if(r<=mid)
		add(k<<1,l,r,x);
	else if(l>mid)
		add(k<<1|1,l,r,x);
	else
		add(k<<1,l,mid,x),add(k<<1|1,mid+1,r,x);
}

inline void tadd(int x,int y,ll val)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]>dep[top[y]]) swap(x,y);
		add(1,idx[top[y]],idx[y],val);
		y=fa[top[y]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	add(1,idx[x],idx[y],val);
}

inline ll qry(int k,int l,int r)
{
	if(tr[k].tag)
		gank(k);
	if(tr[k].l==l && tr[k].r==r)
		return tr[k].sum;
	int mid=(tr[k].l+tr[k].r)>>1;
	if(r<=mid)
		return qry(k<<1,l,r);
	else if(l>mid)
		return qry(k<<1|1,l,r);
	else
		return qry(k<<1,l,mid)+qry(k<<1|1,mid+1,r);
}

inline ll tqry(int x,int y)
{
	ll ret=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]>dep[top[y]]) swap(x,y);
		ret+=qry(1,idx[top[y]],idx[y]);
		y=fa[top[y]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	ret+=qry(1,idx[x],idx[y]);
	return ret;
}

inline void mainwork()
{
	int opt,x;ll w,num=0,sum=0;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&opt,&x);
		if(opt==1)
		{
			scanf("%lld",&w);
			sum+=w-dep[x];
			tadd(1,x,2);++num;
		}
		else if(opt==2)
		{
			w=sum+tqry(1,x)-num*dep[x]+dta[x];
			if(w>0)
				dta[x]-=w;
		}
		else
		{
			ans=sum+tqry(1,x)-num*dep[x]+dta[x];
			printf("%lld\n",ans);
		}
	}
}

int main()
{
	//freopen("C.in","r",stdin);
	//freopen("out.out","w",stdout);
	int t;
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		prework();
		mainwork();
		//print();
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值