2018焦作网络赛

E-Jiu Yuan Wants to Eat

题目链接:https://nanti.jisuanke.com/t/31714

题意:树链剖分4种操作:
1 u到v的节点同时乘上x
2 u到v的节点同时加上x
3 u到v的节点全部64位无符号整型取反
4 求u到v节点的和

树链剖分学了又忘了,来复习一哈

dfs2

在这里插入图片描述

从①开始,一直走,走最长的链走不动了才回头,回头的时候先遇上⑥,然后⑥作为新的起始重节点又继续往下走,走不动了才回头,然后回头的时候又遇上⑦,这样一直下去

UpdateNode

假如他们就在一条链上面,那简单,排的名次是连续的,深度浅一点的排前面

不在一条链上的就麻烦一点了:
在这里插入图片描述
假如要在⑦到⑧节点加权值,先找这两条链的起始重节点,看谁的深度深就先处理谁的,这就好像是先把树的枝条处理了,然后再处理树的主干,起始重节点越深的就越像是树的枝条,而树的主干的起始重节点肯定是最上面,也就是深度最浅的

就像图上这样,先处理黄色的,然后③和⑦比较谁更“枝条”一些就先处理谁

#include"bits/stdc++.h"
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int maxn=2e5+5;
const int MOD=1e9+7;
int N,Q;
struct Edge
{
	int t,v,nxt;
};
Edge E[maxn<<1];
int head[maxn];
int tot;
void AddEdge(int aa,int bb,int val)
{
	E[++tot].t=bb;
	E[tot].v=val;
	E[tot].nxt=head[aa];
	head[aa]=tot;
}
int sz[maxn],fa[maxn],dep[maxn],son[maxn];
void dfs1(int u,int pre,int deep)//处理出节点u的深度dep[u],节点大小sz[u],重儿子son[u]
{
	dep[u]=deep;
	sz[u]=1;
	fa[u]=pre;
	for(int i=head[u]; i!=-1; i=E[i].nxt)
	{
		int t=E[i].t;
		if(t==pre)continue;
		dfs1(t,u,deep+1);
		if(son[u]==-1)son[u]=t;//如果u暂时没有重儿子,那么t就是u的重儿子
		else if(sz[t]>sz[son[u]])son[u]=t;//如果当前这个儿子t比原来的还重,那就更新
		sz[u]+=sz[t];
	}
}
int sa[maxn],rnk[maxn],top[maxn],Time;
void dfs2(int u,int pre,int uu)//处理出u节点的排名rnk[u],sa[u],以及u节点的起始重节点top[u]
{
	rnk[u]=++Time;
	sa[Time]=u;
	top[u]=uu;
	if(son[u]!=-1)dfs2(son[u],u,uu);//先一直往下走,把最长的先找出来
	for(int i=head[u]; i!=-1; i=E[i].nxt)
	{
		int t=E[i].t;
		if(t==pre)continue;
		if(son[u]!=t)dfs2(t,u,t);//如果t这个儿子不是重儿子,那么这个儿子就成了新的起始节点
	}
}
ull sum[maxn<<2],Add[maxn<<2],Mul[maxn<<2];
void pushup(int id)
{
	sum[id]=sum[id<<1]+sum[id<<1|1];
}
void pushdown(int id,int L,int R)
{
	if(Add[id]!=(ull)0||Mul[id]!=(ull)1)
	{
		Add[id<<1]=Add[id<<1]*Mul[id]+Add[id];
		Add[id<<1|1]=Add[id<<1|1]*Mul[id]+Add[id];
		Mul[id<<1]*=Mul[id];
		Mul[id<<1|1]*=Mul[id];
		int mid=L+R>>1;
		sum[id<<1]=sum[id<<1]*Mul[id]+Add[id]*(mid-L+1);
		sum[id<<1|1]=sum[id<<1|1]*Mul[id]+Add[id]*(R-mid);
		Add[id]=0;
		Mul[id]=1;
	}
}
void Build(int id,int L,int R)
{
	sum[id]=0;
	Add[id]=0;
	Mul[id]=1;
	if(L==R)return;
	else
	{
		int mid=L+R>>1;
		Build(id<<1,L,mid);
		Build(id<<1|1,mid+1,R);
		pushup(id);
	}
}
void Update(int id,int L,int R,int qL,int qR,ull add,ull mul)
{
	if(qL<=L&&qR>=R)
	{
		sum[id]=sum[id]*mul+add*(R-L+1);
		Mul[id]*=mul;
		Add[id]=Add[id]*mul+add;
	}
	else
	{
		pushdown(id,L,R);
		int mid=L+R>>1;
		if(qL<=mid)	 Update(id<<1,L    ,mid,qL,qR,add,mul);
		if(qR>=mid+1)Update(id<<1|1,mid+1,R  ,qL,qR,add,mul);
		pushup(id);
	}
}
ull query(int id,int L,int R,int qL,int qR)
{
	if(qL<=L&&qR>=R)return sum[id];
	else
	{
		pushdown(id,L,R);
		ull res=0;
		int mid=L+R>>1;
		if(qL<=mid)  res+=query(id<<1  ,L    ,mid,qL,qR);
		if(qR>=mid+1)res+=query(id<<1|1,mid+1,R  ,qL,qR);
		return res;
	}
}
void UpdateNode(int u,int v,ull add,ull mul)
{
	while(top[u]!=top[v])
	{

		if(dep[top[u]]<dep[top[v]])swap(u,v);//谁的起始重节点深度深,谁就更"枝条",就先处理谁
		Update(1,1,N,rnk[top[u]],rnk[u],add,mul);
		u=fa[top[u]];
	}
	if(rnk[u]>rnk[v])swap(u,v);
	Update(1,1,N,rnk[u],rnk[v],add,mul);
}
ull queryNode(int u,int v)
{
	ull res=0;
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]])swap(u,v);//谁的起始重节点深度深,谁就更"枝条",就先处理谁
		ull tp=query(1,1,N,rnk[top[u]],rnk[u]);
		res+=tp;
		u=fa[top[u]];
	}
	if(rnk[u]>rnk[v])swap(u,v);
	ull tp=query(1,1,N,rnk[u],rnk[v]);
	res+=tp;
	return res;
}
int main()
{

	ull MAX;
	memset(&MAX,-1,sizeof MAX);
	while(cin>>N)
	{
		tot=Time=0;
		memset(head,-1,sizeof head);
		memset(son,-1,sizeof son);
		for(int i=2; i<=N; i++)
		{
			int t;
			scanf("%d",&t);
			AddEdge(i,t,1);
			AddEdge(t,i,1);
		}
		dfs1(1,-1,1);
		dfs2(1,-1,1);
		Build(1,1,N);
		scanf("%d",&Q);
		while(Q--)
		{
			int cmd,u,v;
			ull x;
			scanf("%d%d%d",&cmd,&u,&v);
			if(cmd==1)
			{
				scanf("%lld",&x);
				UpdateNode(u,v,0,x);
			}
			else if(cmd==2)
			{
				scanf("%lld",&x);
				UpdateNode(u,v,x,1);
			}
			else if(cmd==3)
			{

				UpdateNode(u,v,MAX,MAX);
			}
			else 
			{
				printf("%llu\n",queryNode(u,v));
			}
			

		}
	}
}

打印

void print()
{
	for(int i=1; i<=N; i++)cout<<"u="<<i<<" rnk="<<rnk[i]<<" sa="<<sa[i]<<" son="<<son[i]<<" top="<<top[i]<<endl;
}
void printTree()
{
	for(int i=1; i<4*N; i++)
	{
		cout<<"id="<<i<<" Add="<<Add[i]<<" Mul="<<Mul[i]<<" sum="<<sum[i]<<" | ";
		if((i&(i+1))==0)puts("");
	}
	puts("");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值