树链剖分入门指南

最近认真学了一下树剖(重链剖分),这里简单记录一些心得

目录

原理:

入门:

换根操作

边权下放点权

区间覆盖

进阶 


原理:

很多情况下我们需要在树上处理一些路径问题,比如叶子到根节点的路径最大子段和之类的,有时候还要加入修改操作,这时候树剖就是一个非常强有力的武器。对于每一个节点a,定义重儿子为其儿子节点中对应子树最大的儿子,重链为以a为起点的一条链,链中的每一个节点都是一个相应节点的重儿子。如图,红色为重儿子,蓝色路径为重链(还是很好理解的)当然,对于叶子节点来说,它自己本身就是一条重链。

 如此来看,每一个节点都会属于一条重链,那么我们就可以在许多条链上来处理问题了。对于路径相关的问题,我们就可以在链之间切换。

如何每一个节点对应的重链?递归找它的重儿子以及重儿子的重儿子等即可。

如何找重儿子?dfs一遍并记录最大的子树大小即可。

这里我们还顺便记录了深度dep【】

其中son【】就是代表每一个节点的重儿子,没有重儿子的话,son自然记为0

fa【】代表父亲节点,siz【】代表子树大小(显然)

void init_dfs(ll id,ll p,ll depth)
{
	dep[id]=depth;
	siz[id]=1;
	fa[id]=p;
	ll son_val=0;
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(y==p) continue;
		init_dfs(y,id,depth+1);
		siz[id]+=siz[y];
		if(siz[y]>son_val)
		{
			son_val=siz[y];
			son[id]=y;
		}
	}
}

为了快速在重链之间跳转,我们需要记录每一个节点所在重链的起点,记为top【】,很明显重链的top是能不断向下传递记录的,对于轻儿子,它自己是一条重链的起点,所以从它开始top要改变。这里的idx数组记录的是每一个节点的dfn序,因为我们最后是要解决线性问题(路径),那么给他们一个编号就会方便很多。 此外,为了保证重链节点的dfn序的连续性,我们会优先递归重儿子。

void dfs2(ll id,ll p)//节点编号,对应重链的起点
{
	top[id]=p;
	idx[id]=++cnt;
	a[cnt]=mas[id];
	if(!son[id]) return;//如果没有重儿子就退出
	dfs2(son[id],p);//先递归重儿子,top为p
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(!idx[y]) dfs2(y,y);//自己是重链的起点,top记为自己
	}
}

到这里其实就没了,我们就已经成功地把这棵树剖成一条一条链了。

但是为了处理各种各样的区间问题,我们还会引入线段树。利用上文的idx数组,我们发现可以成功地把树上路径转为一维区间,那么就可以维护很多东西了,并且线段树构造与一般的并无区别。这里就不贴了。

还有问题就是查询以及修改。

如果是路径修改/查询的话:

我们自然就是在路径经过的一条条重链上进行处理。

对于每一条需要操作的重链,它是一段连续区间,这是线段树可以解决的问题,那么我们的思路就是让两个点不断跳重链并更新路径上的信息,最后来到同一条重链(top【】值相同)时再修改/查询一次就可以了。

如果是子树修改/查询的话:

子树的dfn序是连续的,所以这就是一个简单的线段树操作(大部分情况下);

下面以求树上路径和为例给出模板

ll tree_sum(ll x,ll y)//树上路径和 
{
	ll ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ans=(ans+sum(1,idx[top[x]],idx[x]))%mod;
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	//来到同一重链 
	ans=(ans+sum(1,idx[x],idx[y]))%mod;
	return ans;
}

void tree_add(ll x,ll y,ll val)
{
	
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		add(1,idx[top[x]],idx[x],val);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	//来到同一重链
	add(1,idx[x],idx[y],val); 
}

void trson_add(ll x,ll val)//以x为根的子树所有权值+=val 
{
	add(1,idx[x],idx[x]+siz[x]-1,val%mod);
}

ll trson_sum(ll x)
{
	return sum(1,idx[x],idx[x]+siz[x]-1);
}

这就是树剖基础。

入门:

板子题

换根操作

大意:

唯一的难处就是换根操作,但其实这个跟普通的换根dp的思想也是一样的,无非就是考虑根改变了之后对其它查询/操作的影响。

首先,换根对路径操作肯定是没有影响的(显然)

考虑其对子树操作的影响:

令root为当前根,x为要求的根,lca为两者的lca

1.u==root:直接输出整棵树的和就可以了

2.lca!=x:x的子树没有变,就正常查询就可以了

3.lca==x:x的子树就变成了x到root路径上第一个儿子的子树的补集(想一想),这时我们找到这个儿子,并求出它的子树和,再用全局和减去它即可。

求lca:

ll get_lca(ll x,ll y)//获取两节点的lca 
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]>dep[y]?y:x;
}

求路径上的第一个儿子:

ll find_firson(ll x,ll y)//找到x到y路径上的第一个儿子
{
    while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		if(fa[top[x]]==y) return top[x];
		x=fa[top[x]];
	}	
	if(dep[x]<dep[y]) swap(x,y);
	return son[y];
} 

code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
inline int read()
{
	int X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}
ll n,m,rt,fir,sec,op,x,y,z;
struct ty{
	ll t,l,next;
}edge[N<<1];
struct tree
{
	ll l,r;
	ll val;
	ll add;//lazy 
	ll siz; 
}tr[N<<2];
ll cn=0;
ll head[N];
void add(ll a,ll b,ll c)
{
	edge[++cn].t=b;
	edge[cn].l=c;
	edge[cn].next=head[a];
	head[a]=cn;
}

ll dep[N];//节点深度
ll fa[N];//节点父亲
ll son[N];//节点重儿子
ll siz[N];//节点子树的大小
ll mas[N],a[N];//初始序列   处理后的序列
ll cnt=0;//dfs序的计数器
ll idx[N];//节点的dfs序 id 
ll top[N];//节点所在链的顶端 要在init_dfs处理之后才能更新这个 

void init_dfs(ll id,ll p,ll depth)//节点 父亲 深度 
{//更新重儿子以及各节点的子树大小&深度 
	dep[id]=depth;
	fa[id]=p;
	siz[id]=1;
	ll son_val=0;//重儿子的子树大小
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(y==p) continue;
		init_dfs(y,id,depth+1);
		siz[id]+=siz[y];//子树大小更新 
		if(siz[y]>son_val)
		{
			son_val=siz[y];
			son[id]=y;
		}
    } 
} 

void dfs2(ll id,ll p)
{
	idx[id]=++cnt;
	a[cnt]=mas[id];//序列转移
	top[id]=p;
	if(!son[id]) return;//无重儿子的情况(也就是无儿子,是叶子节点)直接返回
	//下面的dfs顺序:先遍历重儿子,再遍历轻儿子 
	dfs2(son[id],p);//顶端idx不变
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(!idx[y]) dfs2(y,y);//新的链(轻链) 
	} 
}

void push_up(ll p)
{
	tr[p].val=(tr[p<<1].val+tr[p<<1|1].val);
}

void build(ll p,ll l,ll r)
{
	tr[p].l=l;tr[p].r=r;tr[p].siz=r-l+1;
	if(l==r)
	{
		tr[p].val=a[l];//用新的序列值 
		return;
	}
	ll mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	push_up(p);
}

void push_down(ll p)
{
	if(tr[p].add==0) return;
	tr[p<<1].val=(tr[p<<1].val+tr[p<<1].siz*tr[p].add);
	tr[p<<1|1].val=(tr[p<<1|1].val+tr[p<<1|1].siz*tr[p].add);
	tr[p<<1].add=(tr[p<<1].add+tr[p].add);
	tr[p<<1|1].add=(tr[p<<1|1].add+tr[p].add);
	tr[p].add=0;//下传结束 
}

void add(ll p,ll l,ll r,ll val)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		tr[p].val=(tr[p].val+tr[p].siz*val);
		tr[p].add=(tr[p].add+val);
		return;
	}
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) add(p<<1,l,r,val);
	if(r>=mid+1) add(p<<1|1,l,r,val);
	push_up(p);
}

ll sum(ll p,ll l,ll r)
{
	ll ans=0;
	if(tr[p].l>=l&&tr[p].r<=r) return tr[p].val;
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) ans=(ans+sum(p<<1,l,r));
	if(r>=mid+1) ans=(ans+sum(p<<1|1,l,r));
	return ans;
}

ll tree_sum(ll x,ll y)//树上路径和 
{
	ll ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ans=(ans+sum(1,idx[top[x]],idx[x]));
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	//来到同一重链 
	ans=(ans+sum(1,idx[x],idx[y]));
	return ans;
}

void tree_add(ll x,ll y,ll val)
{
	
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		add(1,idx[top[x]],idx[x],val);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	//来到同一重链
	add(1,idx[x],idx[y],val); 
}

ll get_lca(ll x,ll y)//获取两节点的lca 
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]>dep[y]?y:x;
}

ll find_firson(ll x,ll y)//找到x到y路径上的第一个儿子
{
    while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		if(fa[top[x]]==y) return top[x];
		x=fa[top[x]];
	}	
	if(dep[x]<dep[y]) swap(x,y);
	return son[y];
} 

void trson_add(ll x,ll val)//以x为根的子树所有权值+=val 
{
	if(x==rt)
	{
		add(1,1,n,val);
		return;
	}
	//
	ll LCA=get_lca(x,rt);
	if(LCA!=x) 
	{
		add(1,idx[x],idx[x]+siz[x]-1,val);
		return;
	}
	//
	ll child=find_firson(x,rt);
	add(1,1,n,val);
	add(1,idx[child],idx[child]+siz[child]-1,-val);
	
}

ll trson_sum(ll x)
{
	if(x==rt) return sum(1,1,n);
	///
	ll LCA=get_lca(x,rt);
	if(LCA!=x) return sum(1,idx[x],idx[x]+siz[x]-1);
	///
	ll child=find_firson(x,rt);
	ll res=sum(1,1,n);//整棵树的总和
	ll f_res=sum(1,idx[child],idx[child]+siz[child]-1);
	return res-f_res; 
}


int main()
{
	memset(head,-1,sizeof head);
	n=read();
	rt=1;
	for(int i=1;i<=n;++i) mas[i]=read();
	for(int i=1;i<n;++i)
	{
		fir=read();
		add(fir,i+1,1);
		add(i+1,fir,1);
	}
	init_dfs(rt,0,1);
	dfs2(rt,rt);
	build(1,1,n);
	m=read();
	while(m--)
	{
		op=read();
		if(op==2)
		{
			x=read();y=read();z=read();
			tree_add(x,y,z);
			continue;
		}
		else if(op==4)
		{
			x=read();y=read();
			printf("%lld\n",tree_sum(x,y));
			continue;
		}
		else if(op==3)
		{
			
			x=read();z=read();
			trson_add(x,z);
			continue;
		}
		else if(op==5)
		{
			x=read();
			printf("%lld\n",trson_sum(x));
			continue;
		}
		else if(op==1)
		{
			x=read();
			rt=x;
		}
	}
	return 0;
}

 Max Flow P

 大意:
操作1:路径总体权值+1

操作2:查询路径最大值

思路:

很显然也是板子,线段树维护区间最大值即可

#include<bits/stdc++.h>
using namespace std;
#define ll int
#define endl '\n'
const ll N=5e4+10;
struct ty{
	ll t,l,next;
}edge[N<<1];
ll cn=0;
ll n,k;
ll aa,b,c,rt;
ll head[N];
void add_edge(ll a,ll b,ll c)
{
	edge[++cn].t=b;
	edge[cn].l=c;
	edge[cn].next=head[a];
	head[a]=cn;
}
struct tree
{
	ll l,r;
	ll val;
	ll add;//lazy
	ll siz;
}tr[N<<2];
ll dep[N];//深度
ll fa[N],son[N];
ll siz[N];
ll mas[N],a[N];//初始序列,处理后的序列
ll cnt=0;//dfs序的时间戳
ll top[N];//所在链的顶端
ll idx[N];
void init_dfs(ll id,ll p,ll depth)
{
    dep[id]=depth;
    fa[id]=p;
	siz[id]=1;
	ll son_val=0;
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(y==p) continue;
		init_dfs(y,id,depth+1);
		siz[id]+=siz[y];
		if(siz[y]>son_val)
		{
			son_val=siz[y];
			son[id]=y;
		}
	}		
} 
void dfs2(ll id,ll p)
{//更新链上的情况 
	idx[id]=++cnt;
	a[cnt]=mas[id];
	top[id]=p;
	if(!son[id]) return;
	dfs2(son[id],p);
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		//if(y==p||y==son[id]) continue;
		if(!idx[y]) dfs2(y,y);//新的链 
	}
}
void push_up(ll p)
{
	tr[p].val=max(tr[p<<1].val,tr[p<<1|1].val);
}
void build(ll p,ll l,ll r)
{
	tr[p].l=l;tr[p].r=r;tr[p].siz=r-l+1;
	if(l==r)
	{
		tr[p].val=a[l];
		return;
	}
	ll mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
}
void push_down(ll p)
{
	if(tr[p].add==0) return;
	tr[p<<1].val+=tr[p].add;
	tr[p<<1|1].val+=tr[p].add;
	tr[p<<1].add+=tr[p].add;
	tr[p<<1|1].add+=tr[p].add;
	tr[p].add=0;
}
void add(ll p,ll l,ll r,ll val)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		tr[p].val+=val;
		tr[p].add+=val;
		return;
	}
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) add(p<<1,l,r,val);
	if(r>mid) add(p<<1|1,l,r,val);
	push_up(p);
}
ll sum(ll p,ll l,ll r)
{
	ll ans=0;
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		return tr[p].val;
	}
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) ans=max(ans,sum(p<<1,l,r));
	if(r>mid) ans=max(ans,sum(p<<1|1,l,r));
	return ans;
}
ll tree_sum(ll x,ll y)
{
	ll ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ans=max(ans,sum(1,idx[top[x]],idx[x]));
		x=fa[top[x]];
	}
	if(dep[x]<dep[y]) swap(x,y);
	ans=max(ans,sum(1,idx[x],idx[y]));
	return ans;
}
ll tree_add(ll x,ll y,ll val)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		add(1,idx[top[x]],idx[x],val);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	add(1,idx[x],idx[y],val);
}
void trson_add(ll x,ll val)
{
	add(1,idx[x],idx[x]+siz[x]-1,val);
}
ll trson_sum(ll x)
{
	return sum(1,idx[x],idx[x]+siz[x]-1);
}
void solve()
{
	cin>>n>>k;
	for(int i=1;i<n;++i)
	{
		cin>>aa>>b;
		add_edge(aa,b,1);
		add_edge(b,aa,1);
	}
	init_dfs(rt,0,1);
	dfs2(rt,rt);
	build(1,1,n);
	while(k--)
	{
		cin>>aa>>b;
		tree_add(aa,b,1);
	}
	cout<<sum(1,1,n);
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	memset(head,-1,sizeof head);
	rt=1;
	solve();
	return 0;
}

Grass Planting G

边权下放点权

 大意:

路径上边权+1,查询两点之间的边权。

一个很明显的思路是边权下放点权,详见我的另一篇博客

Cow Land G

大意:

单点修改,路径查询

思路:

#include<bits/stdc++.h>
using namespace std;
#define ll int
#define endl '\n'
const ll N=1e5+10;
struct ty{
	ll t,l,next;
}edge[N<<1];
ll cn=0;
ll head[N];
ll n,q,rt;
ll aa,b,op;
void add_edge(ll a,ll b,ll c)
{
	edge[++cn].l=c;
	edge[cn].t=b;
	edge[cn].next=head[a];
	head[a]=cn;
}
struct tree{
	ll l,r;
	ll val;
	ll add;
	ll siz;
}tr[N<<2];
ll dep[N];
ll siz[N],son[N],fa[N];
ll a[N],mas[N];
ll cnt=0;//dfn时间戳
ll idx[N]; 
ll top[N];
void init_dfs(ll id,ll p,ll depth)
{
	dep[id]=depth;
	fa[id]=p;
	siz[id]=1;
	ll son_size=0;
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(y==p) continue;
		init_dfs(y,id,depth+1);
		siz[id]+=siz[y];
		if(siz[y]>son_size)
		{
			son[id]=y;
			son_size=siz[y];
		}
	}
}
void dfs2(ll id,ll p)
{
	idx[id]=++cnt;
	a[cnt]=mas[id];
	top[id]=p;
	if(!son[id]) return;
	dfs2(son[id],p);
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(!idx[y]) dfs2(y,y);
	}
}
void push_up(ll p)
{
	tr[p].val=(tr[p<<1].val^tr[p<<1|1].val);
}
void build(ll p,ll l,ll r)
{
	tr[p].l=l;tr[p].r=r;tr[p].siz=r-l+1;
	if(l==r)
	{
		tr[p].val=a[l];
		return;
	}
	ll mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	push_up(p);
}
void add(ll p,ll l,ll r,ll val)
{
	if(tr[p].l==tr[p].r)
	{
		tr[p].val=val;
		return;
	}
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) add(p<<1,l,r,val);
	else add(p<<1|1,l,r,val);
	push_up(p);
}
ll sum(ll p,ll l,ll r)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		return tr[p].val;
	}
	ll mid=tr[p].l+tr[p].r>>1;
	ll ans=0;
	if(l<=mid) ans^=sum(p<<1,l,r);
	if(r>mid) ans^=sum(p<<1|1,l,r);
	return ans;
}
void tree_add(ll x,ll y,ll val)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		add(1,idx[top[x]],idx[x],val);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	add(1,idx[x],idx[y],val);
}
ll tree_sum(ll x,ll y)
{
	ll ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ans^=sum(1,idx[top[x]],idx[x]);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	ans^=sum(1,idx[x],idx[y]);
	return ans;
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	memset(head,-1,sizeof head);
	rt=1;
	cin>>n>>q;
	for(int i=1;i<=n;++i) cin>>mas[i];
	for(int i=1;i<n;++i)
	{
		cin>>aa>>b;
		add_edge(aa,b,1);
		add_edge(b,aa,1);
	}
	init_dfs(rt,0,1);
	dfs2(rt,rt);
	build(1,1,n);
	while(q--)
	{
		cin>>op>>aa>>b;
		if(op==1)
		{
			tree_add(aa,aa,b);
		}
		else cout<<tree_sum(aa,b)<<endl;
	}
	return 0; 
}

[HEOI2016/TJOI2016]树

大意:

 思路:

要求祖先最近,也就是求该点到根的路径上深度最深的被标记过的点。

那么线段树记录被标记过的点的dfn序,然后维护区间最大值即可。注意处置记为-1

#include<bits/stdc++.h>
using namespace std;
#define ll int
#define endl '\n'
const ll N=1e5+10;
struct ty{
	ll t,l,next;
}edge[N<<1];
ll cn=0;
ll head[N];
ll n,q,rt;
ll aa,b;
ll sd[N];
char op;
void add_edge(ll a,ll b,ll c)
{
	edge[++cn].l=c;
	edge[cn].t=b;
	edge[cn].next=head[a];
	head[a]=cn;
}
struct tree{
	ll l,r;
	ll val;
	ll add;
	ll siz;
}tr[N<<2];
ll dep[N];
ll siz[N],son[N],fa[N];
ll a[N],mas[N];
ll cnt=0;//dfn时间戳
ll idx[N]; 
ll top[N];
void init_dfs(ll id,ll p,ll depth)
{
	dep[id]=depth;
	fa[id]=p;
	siz[id]=1;
	ll son_size=0;
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(y==p) continue;
		init_dfs(y,id,depth+1);
		siz[id]+=siz[y];
		if(siz[y]>son_size)
		{
			son[id]=y;
			son_size=siz[y];
		}
	}
}
void dfs2(ll id,ll p)
{
	idx[id]=++cnt;
	sd[cnt]=id;
	a[cnt]=mas[id];
	top[id]=p;
	if(!son[id]) return;
	dfs2(son[id],p);
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(!idx[y]) dfs2(y,y);
	}
}
void push_up(ll p)
{
	tr[p].val=max(tr[p<<1].val,tr[p<<1|1].val);
}
void build(ll p,ll l,ll r)
{
	tr[p].l=l;tr[p].r=r;tr[p].siz=r-l+1;
	if(l==r)
	{
		tr[p].val=-1;
		return;
	}
	ll mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	push_up(p);
}
void add(ll p,ll l,ll r,ll val)
{
	if(tr[p].l==tr[p].r)
	{
		if(val) tr[p].val=tr[p].l;
		return;
	}
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) add(p<<1,l,r,val);
	else add(p<<1|1,l,r,val);
	push_up(p);
}
ll sum(ll p,ll l,ll r)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		return tr[p].val;
	}
	ll mid=tr[p].l+tr[p].r>>1;
	ll ans=-1;
	if(l<=mid) ans=max(ans,sum(p<<1,l,r));
	if(r>mid) ans=max(ans,sum(p<<1|1,l,r));
	return ans;
}
void tree_add(ll x,ll y,ll val)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		add(1,idx[top[x]],idx[x],val);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	add(1,idx[x],idx[y],val);
}
ll tree_sum(ll x,ll y)
{
	ll ans=-1;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ans=sum(1,idx[top[x]],idx[x]);
		if(ans!=-1) return sd[ans];
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	ans=sum(1,idx[x],idx[y]);
	return sd[ans];
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	memset(head,-1,sizeof head);
	rt=1;
	cin>>n>>q;
	//for(int i=1;i<=n;++i) cin>>mas[i];
	for(int i=1;i<n;++i)
	{
		cin>>aa>>b;
		add_edge(aa,b,1);
		add_edge(b,aa,1);
	}
	init_dfs(rt,0,1);
	dfs2(rt,rt);
	build(1,1,n);
	add(1,1,1,1);
	while(q--)
	{
		cin>>op>>aa;
		if(op=='C')
		{
			add(1,idx[aa],idx[aa]+1,1);
		//	tree_add(aa,aa,1);
		}
		else cout<<tree_sum(aa,1)<<endl;
	}
	return 0; 
}

月下毛景树

区间覆盖

大意:

操作比较多,有三个修改操作,对于第二/三个操作,我们记录add2,add1(别问我为什么是反的,写的时候脑子丢了吧...).pushdown时很明显先处理add2,因为它代表区间替换,那么之前有没有加的操作并不重要,当然得把add1儿子的add1清零。然后再加上边权与点权的转换就可以了。代码还是有点长的,写起来很酸爽。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
struct ty
{
    ll t,l,next;	
}edge[N<<1];
struct tree
{
	ll l,r;
	ll val;
	ll add1,add2=-1;//加,替换 
	ll siz;
}tr[N<<2];
ll cn=0;
ll head[N];
void add_edge(ll a,ll b,ll c)
{
	edge[++cn].t=b;
	edge[cn].l=c;
	edge[cn].next=head[a];
	head[a]=cn;
}
ll siz[N],fa[N],son[N],dep[N],idx[N],a[N],mas[N],top[N]; 
ll n,m,cnt,rt,fir,sec,thi,x,y,z;
pair<ll,ll> church[N];
string op;
void init_dfs(ll id,ll p,ll depth)
{
	siz[id]=1;
	dep[id]=depth;
	fa[id]=p;
	ll son_val=0;
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(y==p) continue;
		mas[y]=edge[i].l;
		init_dfs(y,id,depth+1);
		siz[id]+=siz[y];
		if(siz[y]>son_val)
		{
			son_val=siz[y];
			son[id]=y;
		}	
	}
}
void dfs2(ll id,ll p)
{
	top[id]=p;
	idx[id]=++cnt;
	a[cnt]=mas[id];
	if(!son[id]) return;
	dfs2(son[id],p);
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(!idx[y]) dfs2(y,y);
	}
}
void push_up(ll p)
{
	tr[p].val=max(tr[p<<1].val,tr[p<<1|1].val);
}
void push_down(ll p)
{
	if(tr[p].add2>=0)
	{
		tr[p<<1].add1=tr[p<<1|1].add1=0;
		tr[p<<1].val=tr[p<<1|1].val=tr[p].add2;
		tr[p<<1].add2=tr[p<<1|1].add2=tr[p].add2;
		tr[p].add2=-1;
	}
	if(tr[p].add1)
	{
		tr[p<<1].add1+=tr[p].add1;
		tr[p<<1|1].add1+=tr[p].add1;
		tr[p<<1].val+=tr[p].add1;
		tr[p<<1|1].val+=tr[p].add1;
		tr[p].add1=0;
	}
}
void build(ll p,ll l,ll r)
{
	tr[p].l=l;tr[p].r=r;
	tr[p].siz=r-l+1;
	if(l==r)
	{
		tr[p].val=a[l];
		tr[p].add1=0;tr[p].add2=-1;
		return;
	}
	ll mid=r+l>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	push_up(p);
}
void ti_huan(ll p,ll l,ll r,ll val)//cover 
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		tr[p].val=val;
		tr[p].add1=0;tr[p].add2=val;
		return;
	}
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) ti_huan(p<<1,l,r,val);
	if(r>mid) ti_huan(p<<1|1,l,r,val);
	push_up(p);
}
void add(ll p,ll l,ll r,ll val)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		tr[p].val+=val;
		tr[p].add1+=val;
		return;
	}
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) add(p<<1,l,r,val);
	if(r>mid) add(p<<1|1,l,r,val);
	push_up(p);
}
ll sum(ll p,ll l,ll r)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		return tr[p].val;
	}
	ll ans=0;
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) ans=max(ans,sum(p<<1,l,r));
	if(r>mid) ans=max(ans,sum(p<<1|1,l,r));
	//cout<<"SUM "<<p<<' '<<l<<' '<<r<<endl;
	return ans;
}
void change(ll p,ll l,ll val)
{
	if(tr[p].l==tr[p].r)
	{
		tr[p].add1=0;
		tr[p].add2=val;
		tr[p].val=val;
		return;
	}
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) change(p<<1,l,val);
	else change(p<<1|1,l,val);
	push_up(p);
}
void tree_cover(ll x,ll y,ll val)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ti_huan(1,idx[top[x]],idx[x],val);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	ti_huan(1,idx[x]+1,idx[y],val);
}
void tree_add(ll x,ll y,ll val)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		add(1,idx[top[x]],idx[x],val);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	add(1,idx[x]+1,idx[y],val);
}
ll tree_sum(ll x,ll y)
{
	ll ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ans=max(ans,sum(1,idx[top[x]],idx[x]));
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	ans=max(ans,sum(1,idx[x]+1,idx[y]));
	return ans;
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	memset(head,-1,sizeof head);
	cin>>n;
	for(int i=1;i<n;++i)
	{
		cin>>fir>>sec>>thi;
		add_edge(fir,sec,thi);
		add_edge(sec,fir,thi);
		church[i]=make_pair(fir,sec);
	}
	rt=1;
	init_dfs(rt,0,1);
	dfs2(rt,rt);
	build(1,1,n);
//	for(int i=1;i<=n;++i) cout<<tree_sum(1,i)<<endl;
	while(cin>>op)
	{
		if(op[0]=='S') break;
		if(op=="Max")
		{
			cin>>x>>y;
			cout<<tree_sum(x,y)<<endl;
			continue;
		}
		if(op=="Cover")
		{
			cin>>x>>y>>z;
			tree_cover(x,y,z);
			continue;
		}
		if(op=="Add")
		{
			cin>>x>>y>>z;
			tree_add(x,y,z);
			continue;
		}
		if(op=="Change")
		{
			cin>>x>>y;
			ll sd;
			if(dep[church[x].first]>dep[church[x].second]) sd=church[x].first;
			else sd=church[x].second;
			change(1,idx[sd],y);
		}
	}
	return 0;
}

树的统计

大意:

思路:

基操,只是维护了两个值而已。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=2e5+10;
struct ty{
	ll t,l,next;
}edge[N<<1];
ll head[N];
ll cn=0;
void add_edge(ll a,ll b,ll c)
{
	edge[++cn].t=b;
	edge[cn].l=c;
	edge[cn].next=head[a];
	head[a]=cn;
}
struct tree
{
	ll l,r;
	ll val;
	ll maxn;
	ll add;
	ll siz;
}tr[N<<2];
ll siz[N],fa[N],idx[N],a[N],mas[N],top[N],son[N],dep[N];
ll n,m,rt,fir,sec,x,y,z,cnt;
string op;
void init_dfs(ll id,ll p,ll depth)
{
	fa[id]=p;
	dep[id]=depth;
	siz[id]=1;
	ll son_val=0;
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(y==p) continue;
		init_dfs(y,id,depth+1);
		siz[id]+=siz[y];
		if(siz[y]>=son_val)
		{
			son_val=siz[y];
			son[id]=y;			
		}
	}
}
void dfs2(ll id,ll p)
{
	top[id]=p;
	idx[id]=++cnt;
	a[cnt]=mas[id];
	if(!son[id]) return;
	dfs2(son[id],p);
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(!idx[y]) dfs2(y,y);
	}
}
void push_up(ll p)
{
	tr[p].maxn=max(tr[p<<1].maxn,tr[p<<1|1].maxn);
	tr[p].val=tr[p<<1].val+tr[p<<1|1].val;
}
void build(ll p,ll l,ll r)
{
	tr[p].l=l;tr[p].r=r;tr[p].siz=r-l+1;
	if(l==r)
	{
		tr[p].maxn=tr[p].val=a[l];
		return;
	}
	ll mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	push_up(p);
}
void change(ll p,ll l,ll val)
{
	if(tr[p].l==tr[p].r)
	{
		tr[p].val=tr[p].maxn=val;
		return;
	}
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) change(p<<1,l,val);
	else change(p<<1|1,l,val);
	push_up(p);
}
ll sum(ll p,ll l,ll r)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		return tr[p].val;
	}
	ll mid=tr[p].l+tr[p].r>>1;
	ll ans=0;
	if(l<=mid) ans+=sum(p<<1,l,r);
	if(r>mid) ans+=sum(p<<1|1,l,r);
	return ans; 
}
ll maxn(ll p,ll l,ll r)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		return tr[p].maxn;
	}
	ll mid=tr[p].l+tr[p].r>>1;
	ll ans=-1e15;
	if(l<=mid) ans=max(ans,maxn(p<<1,l,r));
	if(r>mid) ans=max(ans,maxn(p<<1|1,l,r));
	return ans;
}
ll tree_sum(ll x,ll y)
{
	ll ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ans+=sum(1,idx[top[x]],idx[x]);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	ans+=sum(1,idx[x],idx[y]);
	return ans;
}
ll tree_maxn(ll x,ll y)
{
	ll ans=-1e15;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ans=max(ans,maxn(1,idx[top[x]],idx[x]));
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	ans=max(ans,maxn(1,idx[x],idx[y]));
	return ans;
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	memset(head,-1,sizeof head);
	cin>>n;
	for(int i=1;i<n;++i)
	{
		cin>>fir>>sec;
		add_edge(fir,sec,1);
		add_edge(sec,fir,1);
	}
	for(int i=1;i<=n;++i) cin>>mas[i];
	cin>>m;
	rt=1;
	init_dfs(rt,0,1);
	dfs2(rt,rt);
	build(1,1,n);
	for(int i=1;i<=m;++i)
	{
		cin>>op;
		cin>>x>>y;
		if(op[0]=='C')
		{
			change(1,idx[x],y);
			continue;
		}
		else if(op[1]=='M')
		{
			cout<<tree_maxn(x,y)<<endl;
		}
		else cout<<tree_sum(x,y)<<endl;
	}
	return 0;
}

 [SHOI2012]魔法树

大意:

区间加,子树查询

思路:

还是板子

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=2e5+10;
struct ty
{
	ll t,l,next;
}edge[N<<1];
ll head[N];
ll cn=0;
void add_edge(ll a,ll b,ll c)
{
	edge[++cn].t=b;
	edge[cn].next=head[a];
	edge[cn].l=c;
	head[a]=cn;
}
struct tree
{
	ll l,r;
	ll val;
	ll add;
	ll siz;
}tr[N<<2];
ll son[N],fa[N],siz[N],top[N],dep[N],idx[N],a[N],mas[N];
ll cnt=0,n,m,rt,fir,sec,x,y,z;
char op;
void init_dfs(ll id,ll p,ll depth)
{
	dep[id]=depth;
	fa[id]=p;
	siz[id]=1;
	ll son_val=0;
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(y==p) continue;
		init_dfs(y,id,depth+1);
		siz[id]+=siz[y];
		if(siz[y]>son_val)
		{
			son_val=siz[y];
			son[id]=y;
		}
	}
}
void dfs2(ll id,ll p)
{
	top[id]=p;
	idx[id]=++cnt;
	a[cnt]=mas[id];
	if(!son[id]) return;
	dfs2(son[id],p);
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(!idx[y]) dfs2(y,y);
	}
}
void push_up(ll p)
{
	tr[p].val=tr[p<<1].val+tr[p<<1|1].val;
}
void push_down(ll p)
{
	if(tr[p].add==0) return;
	tr[p<<1].val+=tr[p<<1].siz*tr[p].add;
	tr[p<<1|1].val+=tr[p<<1|1].siz*tr[p].add;
	tr[p<<1].add+=tr[p].add;
	tr[p<<1|1].add+=tr[p].add;
	tr[p].add=0;
}
void build(ll p,ll l,ll r)
{
	tr[p].l=l;tr[p].r=r;tr[p].siz=r-l+1;
	if(l==r)
	{
		tr[p].val=a[l];
		return;
	}
	ll mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	push_up(p);
}
void add(ll p,ll l,ll r,ll val)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		tr[p].val+=tr[p].siz*val;
		tr[p].add+=val;
		return;
	}
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) add(p<<1,l,r,val);
	if(r>mid) add(p<<1|1,l,r,val);
	push_up(p);
}
ll sum(ll p,ll l,ll r)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		return tr[p].val;
	}
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	ll ans=0;
	if(l<=mid) ans+=sum(p<<1,l,r);
	if(r>mid) ans+=sum(p<<1|1,l,r);
	return ans;
}
void tree_add(ll x,ll y,ll val)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		add(1,idx[top[x]],idx[x],val);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	add(1,idx[x],idx[y],val);
}
ll tree_sum(ll x,ll y)
{
	ll ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ans+=sum(1,idx[top[x]],idx[x]);
		x=fa[top[x]]; 
	}
	if(dep[x]>dep[y]) swap(x,y);
	ans+=sum(1,idx[x],idx[y]);
	return ans;
}
ll trson_sum(ll x)
{
	return sum(1,idx[x],idx[x]+siz[x]-1);
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	memset(head,-1,sizeof head);
	cin>>n;
	for(int i=1;i<n;++i)
	{
		cin>>fir>>sec;
		fir++;sec++;
		add_edge(fir,sec,1);
		add_edge(sec,fir,1);
	}
	cin>>m;
	rt=1;
	init_dfs(rt,0,1);
	dfs2(rt,rt);
	build(1,1,n);
	while(m--)
	{
		cin>>op;
		if(op=='A')
		{
			cin>>x>>y>>z;
			x++;y++;
			tree_add(x,y,z);
			continue;
		}
		cin>>x;
		x++;
		cout<<trson_sum(x)<<endl;
	}
	return 0;
}

[NOI2015] 软件包管理器

 大意:
install操作:查询节点到根的权值为0的个数,并区间置1

unstall操作:查询子树权值为1的个数,并区间置0

思路:

区间置操作是很简单的,没什么好讲

对于第一种操作,记录一下修改前的值,那么区间长度减去它就是答案了

对于第二种操作,直接返回区间和就可以了

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
struct ty
{
	ll t,l,next;
}edge[N<<1];
ll cn=0;
ll head[N];
void add_edge(ll a,ll b,ll c)
{
	edge[++cn].t=b;
	edge[cn].l=c;
	edge[cn].next=head[a];
	head[a]=cn;
}
struct tree
{
	ll l,r,siz=0;
	ll add=0;
	ll val=0;
}tr[N<<2];
ll siz[N],dep[N],son[N],fa[N],idx[N],mas[N],a[N],top[N];
ll n,m,fir,sec,cnt,x,y,z,rt;
string op;
void init_dfs(ll id,ll p,ll depth)
{
	fa[id]=p;
	dep[id]=depth;
	siz[id]=1;
	ll son_val=0;
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(y==p) continue;
		init_dfs(y,id,depth+1);
		siz[id]+=siz[y];
		if(siz[y]>son_val)
		{
			son_val=siz[y];
			son[id]=y;
		}
	}
}
void dfs2(ll id,ll p)
{
	top[id]=p;
	idx[id]=++cnt;
	a[cnt]=mas[id];
	if(!son[id]) return;
	dfs2(son[id],p);
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(!idx[y]) dfs2(y,y);
	}
}
void push_up(ll p)
{
	tr[p].val=tr[p<<1].val+tr[p<<1|1].val;
}
void build(ll p,ll l,ll r)
{
	tr[p].l=l;tr[p].r=r;
	tr[p].siz=r-l+1;
	if(l==r)
	{
		tr[p].val=0;
		tr[p].add=-1;
		return;
	}
	ll mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
}
void push_down(ll p)
{
	if(tr[p].add==-1) return;
	tr[p<<1].add=tr[p<<1|1].add=tr[p].add;
	if(tr[p].add==0)
	{
		tr[p<<1].val=tr[p<<1|1].val=0;
	}
	else if(tr[p].add==1)
	{
		tr[p<<1].val=tr[p<<1].siz;
		tr[p<<1|1].val=tr[p<<1|1].siz;
	}
	tr[p].add=-1;
}
ll get_sum(ll p,ll l,ll r)//子树使用,途径置0 
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		ll ans=tr[p].val;
		tr[p].val=0;
		tr[p].add=0;
		return ans;
	}
	push_down(p);
	ll ans=0;
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) ans+=get_sum(p<<1,l,r);
	if(r>mid) ans+=get_sum(p<<1|1,l,r);
	push_up(p);
	return ans;
}
ll sum(ll p,ll l,ll r)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		ll ans=tr[p].val;
		tr[p].val=tr[p].siz;
		tr[p].add=1;
		return tr[p].siz-ans;
	}
	push_down(p);
	ll ans=0;
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) ans+=sum(p<<1,l,r);
	if(r>mid) ans+=sum(p<<1|1,l,r);
	push_up(p);
	return ans;
}
ll trson_sum(ll x)
{
	return get_sum(1,idx[x],idx[x]+siz[x]-1);
}
ll tree_sum(ll x,ll y)
{
	ll ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ans+=sum(1,idx[top[x]],idx[x]);
		x=fa[top[x]]; 
	}
	if(dep[x]>dep[y]) swap(x,y);
	ans+=sum(1,idx[x],idx[y]);
	return ans;
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	memset(head,-1,sizeof head);
	cin>>n;
	for(int i=2;i<=n;++i)
	{
		cin>>fir;
		add_edge(fir+1,i,1);
		add_edge(i,fir+1,1);
	}
	rt=1;
	init_dfs(rt,0,1);
	dfs2(rt,rt);
	build(1,1,n);
//	for(int i=1;i<=n;++i) cout<<tree_sum(i,1)<<' ';
//	cout<<endl;
	cin>>m;
	while(m--)
	{
		cin>>op;
		cin>>x;
		x++;
		if(op[0]=='i')
		{
			cout<<tree_sum(x,1)<<endl;
			continue;
		}
		else cout<<trson_sum(x)<<endl;
	}
}

树上操作

大意:

思路:

大同小异

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
struct ty
{
	ll t,l,next;
}edge[N<<1];
ll cn=0;
ll head[N];
void add_edge(ll a,ll b,ll c)
{
	edge[++cn].t=b;
	edge[cn].l=c;
	edge[cn].next=head[a];
	head[a]=cn;
}
struct tree
{
	ll sum=0;
	ll add=0;
	ll l,r,size=0;
}tr[N<<2];
ll siz[N],son[N],fa[N],dep[N],mas[N],a[N],idx[N],top[N];
ll n,m,rt,cnt,fir,sec,x,y,z,op;
void init_dfs(ll id,ll p,ll depth)
{
	dep[id]=depth;
	siz[id]=1;
	fa[id]=p;
	ll son_val=0;
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(y==p) continue;
		init_dfs(y,id,depth+1);
		siz[id]+=siz[y];
		if(siz[y]>son_val)
		{
			son_val=siz[y];
			son[id]=y;
		}
	}
}
void dfs2(ll id,ll p)
{
	idx[id]=++cnt;
	top[id]=p;
	a[cnt]=mas[id];
	if(!son[id]) return;
	dfs2(son[id],p);
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(!idx[y]) dfs2(y,y);
	}
 } 
 void push_up(ll p)
 {
 	tr[p].sum=tr[p<<1].sum+tr[p<<1|1].sum;
 }
 void push_down(ll p)
 {
 	if(tr[p].add==0) return;
 	tr[p<<1].sum+=tr[p<<1].size*tr[p].add;
 	tr[p<<1|1].sum+=tr[p<<1|1].size*tr[p].add;
 	tr[p<<1].add+=tr[p].add;
 	tr[p<<1|1].add+=tr[p].add;
 	tr[p].add=0;
 }
 void build(ll p,ll l,ll r)
 {
 	tr[p].l=l;tr[p].r=r;tr[p].size=r-l+1;
 	if(l==r)
 	{
 		tr[p].sum=a[l];
 		return;
	}
	ll mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	push_up(p);
 }
 void change(ll p,ll l,ll val)
 {
 	if(tr[p].l==tr[p].r)
 	{
 		tr[p].sum+=val;
 		return;
	}
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) change(p<<1,l,val);
	else change(p<<1|1,l,val);
	push_up(p);
 }
 void add(ll p,ll l,ll r,ll val)
 {
 	if(tr[p].l>=l&&tr[p].r<=r)
 	{
 		tr[p].sum+=tr[p].size*val;
 		tr[p].add+=val;
 		return;
	}
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) add(p<<1,l,r,val);
	if(r>mid) add(p<<1|1,l,r,val);
	push_up(p);
 }
 ll sum(ll p,ll l,ll r)
 {
 	if(tr[p].l>=l&&tr[p].r<=r)
 	{
 		return tr[p].sum;
	}
	ll ans=0;
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) ans+=sum(p<<1,l,r);
	if(r>mid) ans+=sum(p<<1|1,l,r);
	return ans;
 }
ll tree_sum(ll x,ll y)
{
	ll ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ans+=sum(1,idx[top[x]],idx[x]);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	ans+=sum(1,idx[x],idx[y]);
	return ans;
}
void trson_add(ll x,ll val)
{
	add(1,idx[x],idx[x]+siz[x]-1,val);
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	memset(head,-1,sizeof head);
	cin>>n>>m;
	for(int i=1;i<=n;++i) cin>>mas[i];
	for(int i=1;i<n;++i)
	{
		cin>>fir>>sec;
		add_edge(fir,sec,1);
		add_edge(sec,fir,1);
	}
	rt=1;
	init_dfs(rt,0,1);
	dfs2(rt,rt);
	build(1,1,n);
	while(m--)
	{
		cin>>op;
		if(op==1)
		{
			cin>>x>>y;
			change(1,idx[x],y);
			continue;
		}
		if(op==2)
		{
			cin>>x>>y;
			trson_add(x,y);
			continue;
		}
		if(op==3)
		{
			cin>>x;
			cout<<tree_sum(x,1)<<endl;
		}
		
	}
	return 0;
}

进阶 

[USACO18OPEN]Disruption P

大意:

给定一棵树,以及一些多余的边。

对于每一条树上的边,可以取一条多余的边来替代它,使得该边两端的点仍然联通。如果该操作可行,求出改变后的两点最短距离 .不行的话,输出-1 

思路:

正向思考可能比较难,因为对于每一条边都要考虑所有多余的边能否替换它。既然如此,不妨换个思路,考虑每一条边能更新什么。

不难发现,对于一条多余的边,它的两边的点的路径与它可以构成一个环(见图)。环上的点都是可以相互到达的,所以这条多余的边就可以更新环上的所有边。如果我们在每一条边维护一下可以更新它的边的最小边权,这样的话,不就是路径更新最小值嘛。为了使用树剖,我们再记录一下每一条边对应的较深的节点,那么边就与节点建立了一对一的映射,就可以快乐树剖了。

当然也可以不用树剖。我们发现这个过程还是有很多重复的操作,可以考虑使用并查集。

对于所有多余的边,按边权从小到大排序,那么每一条边就尽可能更新它能更新的边,如果某一条边发现已经被更新过了,就跳过,因为它肯定是被更小的边更新的,由该边更新,结果不会更优。这个跳过的操作,用并查集维护就可以了。

求lca时也可以用一下树剖

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
struct ty
{
	ll t,l,next;
}edge[N<<1];
ll cn=0;
ll head[N];
void add(ll a,ll b,ll c)
{
	edge[++cn].t=b;
	edge[cn].l=c;
	edge[cn].next=head[a];
	head[a]=cn;
}
struct road
{
	ll x,y;
	ll val;
}q[N];
bool cmp(road a,road b)
{
	return a.val<b.val;
}
ll siz[N],fa[N],dep[N],top[N],idx[N],a[N],mas[N],son[N];
ll cnt,fir,sec,n,m,rt,x,y,z;
map<pair<ll,ll>,ll> mp;
map<ll,ll> ms;
void init_dfs(ll id,ll p,ll depth)
{
	fa[id]=p;
	siz[id]=1;
	ms[id]=mp[make_pair(id,p)];//让儿子与边建立映射
	dep[id]=depth+1;
	ll son_val=0;
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(y==p) continue;
		init_dfs(y,id,depth+1);
		siz[id]+=siz[y];
		if(siz[y]>son_val)
		{
			son_val=siz[y];
			son[id]=y;
		}
	}
}
void dfs2(ll id,ll p)
{
	top[id]=p;
	idx[id]=++cnt;
	a[cnt]=mas[id];
	if(!son[id]) return;
	dfs2(son[id],p);
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(!idx[y]) dfs2(y,y);
	}
}
ll get_lca(ll x,ll y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]>dep[y]?y:x;
}
ll f[N];
ll find(ll x)
{
	return f[x]==x?f[x]:f[x]=find(f[x]);
}
ll ans[N];
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	memset(head,-1,sizeof head);
	memset(ans,-1,sizeof ans);
	cin>>n>>m;
	for(int i=1;i<n;++i)
	{
		cin>>fir>>sec;
		add(fir,sec,1);
		add(sec,fir,1);
		mp[make_pair(fir,sec)]=i;//边与两个点建立映射
		mp[make_pair(sec,fir)]=i;
	}
	rt=1;
	init_dfs(rt,0,1);
	dfs2(rt,rt);
	for(int i=1;i<=m;++i)
	{
		cin>>q[i].x>>q[i].y>>q[i].val;
	}
	sort(q+1,q+1+m,cmp);
	for(int i=1;i<=n;++i) f[i]=i;
	for(int i=1;i<=m;++i)
	{
		ll fx=find(q[i].x);ll fy=find(q[i].y);
		ll LCA=get_lca(fx,fy);
		while(dep[fx]>dep[LCA])
		{
			//cout<<fx<<' '<<ms[fx]<<' '<<q[i].val<<endl;
			ans[ms[fx]]=q[i].val;
			f[fx]=find(fa[fx]);//注意要跳到父亲节点的对应的第一个可以更新的点
			fx=f[fa[fx]];
		} 
		while(dep[fy]>dep[LCA])
		{
			//cout<<fy<<' '<<ms[fy]<<" "<<q[i].val<<endl;
			ans[ms[fy]]=q[i].val;
			f[fy]=find(fa[fy]);//注意要跳到父亲节点的对应的第一个可以更新的点
			fy=f[fa[fy]];
		}
		
	}
	for(int i=1;i<n;++i) cout<<ans[i]<<endl; 
	return 0;
}

 [SDOI2011]染色

大意:

区间覆盖,求区间颜色段数

思路:

 维护一下线段树的左端点和右端点的颜色,每次合并的时候,让两个子区间的val值相加,如果左儿子的右端点颜色=右儿子左端点颜色,就让val-1即可。

那么查询的时候,就把每一条经过的重链区间的val相加,同样的,如果某一条链的top的颜色=其父节点的颜色,val-1 

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
struct ty
{
	ll t,l,next;
}edge[N<<1];
ll cn=0;
ll head[N];
void add_edge(ll a,ll b,ll c)
{
	edge[++cn].t=b;
	edge[cn].l=c;
	edge[cn].next=head[a];
	head[a]=cn;
}
struct tree
{
	ll l,r;
	ll lm,rm;
	ll siz;
	ll add;
	ll val; 
}tr[N<<2];
ll siz[N],fa[N],top[N],son[N],a[N],idx[N],mas[N],dep[N];
ll n,m,rt,cnt,fir,sec,x,y,z;
char op;
void init_dfs(ll id,ll p,ll depth)
{
	dep[id]=depth;
	fa[id]=p;
	siz[id]=1;
	ll son_val=0;
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(y==p) continue;
		init_dfs(y,id,depth+1);
		siz[id]+=siz[y];
		if(siz[y]>son_val)
		{
			son_val=siz[y];
			son[id]=y;
		}
	}
}
void dfs2(ll id,ll p)
{
	top[id]=p;
	idx[id]=++cnt;
	a[cnt]=mas[id];
	if(!son[id]) return;
	dfs2(son[id],p);
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(!idx[y]) dfs2(y,y);
	}
}
void push_up(ll p)
{
	tr[p].val=tr[p<<1].val+tr[p<<1|1].val;
	if(tr[p<<1].rm==tr[p<<1|1].lm) tr[p].val-=1;
	tr[p].lm=tr[p<<1].lm;
	tr[p].rm=tr[p<<1|1].rm;
}
void push_down(ll p)
{
	if(tr[p].add==0) return;
	tr[p<<1].add=tr[p<<1|1].add=tr[p].add;
	tr[p<<1].lm=tr[p<<1].rm=tr[p].add;
	tr[p<<1|1].lm=tr[p<<1|1].rm=tr[p].add;
	tr[p<<1].val=tr[p<<1|1].val=1;
	tr[p].add=0;
}
void build(ll p,ll l,ll r)
{
	tr[p].l=l;tr[p].r=r;tr[p].siz=r-l+1;
	if(l==r)
	{
		tr[p].val=1;
		tr[p].lm=tr[p].rm=a[l];
		return;
	}
	ll mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	push_up(p);
}
void change(ll p,ll l,ll r,ll val)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		tr[p].val=1;
		tr[p].lm=tr[p].rm=val;
		tr[p].add=val;
		return;
	}
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(r<=mid)
	{
		change(p<<1,l,r,val);
	}
	else if(l>mid)
	{
		change(p<<1|1,l,r,val);
	}
	else
	{
		change(p<<1,l,r,val);
		change(p<<1|1,l,r,val);
	}
//	if(l<=mid) change(p<<1,l,r,val);
//	if(r>mid) change(p<<1|1,l,r,val);
	push_up(p);
}
ll sum(ll p,ll l,ll r)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		return tr[p].val;
	}
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	ll ans=0;
	if(r<=mid) return sum(p<<1,l,r);
	if(l>mid) return sum(p<<1|1,l,r);
	ans+=sum(p<<1,l,r);ans+=sum(p<<1|1,l,r);
	if(tr[p<<1].rm==tr[p<<1|1].lm) ans--;
	return ans;
}
ll col(ll p,ll l)
{
	if(tr[p].l==tr[p].r&&tr[p].l==l)
	{
		return tr[p].lm;
	}
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) return col(p<<1,l);
	else return col(p<<1|1,l);
}
void tree_change(ll x,ll y,ll val)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		change(1,idx[top[x]],idx[x],val);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	change(1,idx[x],idx[y],val);
}
ll tree_sum(ll x,ll y)
{
	ll ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ans+=sum(1,idx[top[x]],idx[x]);
		if(col(1,idx[top[x]])==col(1,idx[fa[top[x]]])) ans--;
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	ans+=sum(1,idx[x],idx[y]);
	return ans;
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	//注意push_up时对左右边界的更新 
	memset(head,-1,sizeof head);
	cin>>n>>m;
	for(int i=1;i<=n;++i) cin>>mas[i];
	for(int i=1;i<n;++i)
	{
		cin>>fir>>sec;
		add_edge(fir,sec,1);
		add_edge(sec,fir,1);
	}
	rt=1;
	init_dfs(rt,0,1);
	dfs2(rt,rt);
	build(1,1,n);
//	cout<<tr[1].lm<<' '<<tr[1].rm<<endl;
	while(m--)
	{
		cin>>op;
		if(op=='C')
		{
			cin>>x>>y>>z;
			tree_change(x,y,z);
			continue;
		}
		cin>>x>>y;
		cout<<tree_sum(x,y)<<endl;
	}
}

遥远的国度

大意:

换根,区间覆盖,查询子树最小值 

思路:

讲一下换根。其实跟第一题的区别不算很大 

令root为当前根,x为要求的根,lca为两者的lca

1.u==root:直接输出整棵树的最小值就可以了

2.lca!=x:x的子树没有变,就正常查询就可以了

3.lca==x:x的子树就变成了x到root路径上第一个儿子的子树的补集,考虑到子树的dfn序是连续的,所以我们可以把查询区间分成两个部分,一个是dfn序小于第一个儿子子树dfn序的区间,一个是dfn序大于第一个儿子子树dfn序的区间。然后合并求最小值就可以了啦

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
struct ty
{
	ll t,l,next;
}edge[N<<1];
ll cn=0;
ll head[N];
void add_edge(ll a,ll b,ll c)
{
	edge[++cn].t=b;
	edge[cn].l=c;
	edge[cn].next=head[a];
	head[a]=cn;
}
struct tree
{
	ll l,r;
	ll siz;
	ll add=-1;
	ll val;
}tr[N<<2];
ll siz[N],fa[N],son[N],dep[N],idx[N],a[N],mas[N],top[N];
ll n,m,rt,cnt,fir,sec,x,y,z,op;
void init_dfs(ll id,ll p,ll depth)
{
	dep[id]=depth;
	fa[id]=p;
	siz[id]=1;
	ll son_val=0;
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(y==p) continue;
		init_dfs(y,id,depth+1);
		siz[id]+=siz[y];
		if(siz[y]>son_val)
		{
			son_val=siz[y];
			son[id]=y;
		}
	}
}
void dfs2(ll id,ll p)
{
	top[id]=p;
	idx[id]=++cnt;
	a[cnt]=mas[id];
	if(!son[id]) return;
	dfs2(son[id],p);
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(!idx[y]) dfs2(y,y);
	}
}
void push_up(ll p)
{
	tr[p].val=min(tr[p<<1].val,tr[p<<1|1].val);
}
void push_down(ll p)
{
	if(tr[p].add==-1) return;
	tr[p<<1].add=tr[p<<1|1].add=tr[p].add;
	tr[p<<1].val=tr[p<<1|1].val=tr[p].add;
	tr[p].add=-1;
}
void build(ll p,ll l,ll r)
{
	tr[p].l=l;tr[p].r=r;tr[p].siz=r-l+1;
	if(l==r)
	{
		tr[p].val=a[l];
		return;
	}
	ll mid=r+l>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	push_up(p); 
}
void change(ll p,ll l,ll r,ll val)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		tr[p].add=tr[p].val=val;
		return;
	}
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) change(p<<1,l,r,val);
	if(r>mid) change(p<<1|1,l,r,val);
	push_up(p);
}
void tree_change(ll x,ll y,ll val)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		change(1,idx[top[x]],idx[x],val); 
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	change(1,idx[x],idx[y],val);
}
ll sum(ll p,ll l,ll r)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		return tr[p].val;
	}
	ll ans=1e16;
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) ans=min(ans,sum(p<<1,l,r));
	if(r>mid) ans=min(ans,sum(p<<1|1,l,r));
	return ans;
}
ll get_lca(ll x,ll y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]>dep[y]?y:x;
}
ll find_firson(ll x,ll y)//找打x到y路径上的第一个儿子 
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		if(fa[top[x]]==y) return top[x];
		x=fa[top[x]];
	} 
	if(dep[x]>dep[y]) swap(x,y); 
	return son[x];
}
ll trson_sum(ll x)
{
	if(x==rt) return sum(1,1,n);
	ll LCA=get_lca(x,rt);
	if(LCA!=x) return sum(1,idx[x],idx[x]+siz[x]-1);
	ll fir_son=find_firson(x,rt);
	ll ans1=sum(1,1,idx[fir_son]-1);
	ll ans2=sum(1,idx[rt]+siz[rt],n);
	return min(ans1,ans2);
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 
	memset(head,-1,sizeof head);
	cin>>n>>m;
	for(int i=1;i<n;++i)
	{
		cin>>fir>>sec;
		add_edge(fir,sec,1);
		add_edge(sec,fir,1);
	} 
	for(int i=1;i<=n;++i) cin>>mas[i];
	cin>>rt;
	init_dfs(1,0,1);
	dfs2(1,1);
	build(1,1,n);
	while(m--)
	{
		cin>>op;
		if(op==1)
		{
			cin>>x;
			rt=x;
			continue;
		}
		else if(op==2)
		{
			cin>>x>>y>>z;
			tree_change(x,y,z);
			continue;
		}
		else
		{
			cin>>x;
			cout<<trson_sum(x)<<endl;
		}
	}
}

[USACO19DEC]Milk Visits G

大意:

对一段区间,查询是否存在特定值

思路:

直接维护所有颜色是不现实的,所以不妨将答案离线下来,按大小升序排序。那么,每次遍历到一种颜色的时候,我们再在线段树上更新对应的颜色,如此,我们只要维护区间最大值就可以了,如果最大值=颜色,就是可以的,否则一定不行,那么就没了(还是钻了没有修改操作的空子嘿嘿) 

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
struct ty
{
	ll t,l,next;
}edge[N<<1];
ll cn=0;
ll head[N]; 
void add_edge(ll a,ll b,ll c)
{
	edge[++cn].t=b;
	edge[cn].l=c;
	edge[cn].next=head[a];
	head[a]=cn;
}
struct tree
{
	ll l,r;
	ll val;
	ll add;
	ll siz;
}tr[N<<2];
vector<ll> color[N];
ll siz[N],dep[N],top[N],fa[N],son[N],a[N],mas[N],idx[N];
ll n,m,fir,sec,cnt,x,y,z,rt;
ll ans[N];
bool vis[N];
struct query
{
	ll x,y,z;
	ll id;
}q[N];
bool cmp(query a,query b)
{
	return a.z<b.z;
}
void init_dfs(ll id,ll p,ll depth)
{
	dep[id]=depth;
	fa[id]=p;
	siz[id]=1;
	ll son_val=0;
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(y==p) continue;
		init_dfs(y,id,depth+1);
		siz[id]+=siz[y];
		if(siz[y]>son_val)
		{
			son_val=siz[y];
			son[id]=y;
		}
	}
}
void dfs2(ll id,ll p)
{
	top[id]=p;
	idx[id]=++cnt;
	a[cnt]=mas[id];
	if(!son[id]) return;
	dfs2(son[id],p);
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(!idx[y]) dfs2(y,y);
	}
}
void push_up(ll p)
{
	tr[p].val=max(tr[p<<1].val,tr[p<<1|1].val);
}
void build(ll p,ll l,ll r)
{
	tr[p].l=l;tr[p].r=r;
	tr[p].siz=r-l+1;
	if(l==r){
		tr[p].val=a[l];
		return;
	}
	ll mid=tr[p].l+tr[p].r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	push_up(p);
}
void change(ll p,ll l,ll r,ll val)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		tr[p].val=val;
		return;
	}
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) change(p<<1,l,r,val);
	if(r>mid) change(p<<1|1,l,r,val);
	push_up(p); 
}
ll sum(ll p,ll l,ll r,ll val)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		return tr[p].val==val;
	}
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) if(sum(p<<1,l,r,val)) return 1;
	if(r>mid) if(sum(p<<1|1,l,r,val)) return 1;
	return 0;
}
ll tree_sum(ll x,ll y,ll val)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		if(sum(1,idx[top[x]],idx[x],val)) return 1;
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	if(sum(1,idx[x],idx[y],val)) return 1;
	return 0;
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	memset(head,-1,sizeof head);
	cin>>n>>m;
	for(int i=1;i<=n;++i)
	{
		cin>>x;
		color[x].push_back(i);
	}
	for(int i=1;i<n;++i)
	{
		cin>>fir>>sec;
		add_edge(fir,sec,1);
		add_edge(sec,fir,1);
	}
	for(int i=1;i<=m;++i)
	{
		cin>>q[i].x>>q[i].y>>q[i].z;
		q[i].id=i;
	}
	rt=1;
	init_dfs(rt,0,1);
	dfs2(rt,rt);
	build(1,1,n);
	sort(q+1,q+1+m,cmp);
	for(int i=1;i<=m;++i)
	{
		ll col=q[i].z;
		if(color[col].size()==0) continue;
		if(!vis[col])
		{
			for(int k:color[col])
			{
				change(1,idx[k],idx[k],col);
			}
		}
		vis[col]=1;
		ans[q[i].id]=tree_sum(q[i].x,q[i].y,q[i].z);
	}
	for(int i=1;i<=m;++i) cout<<ans[i];
}

更新中...

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值