HYSBZ - 2243 染色 (树链剖分)

本文介绍了一种基于树状数组实现的区间更新与区间查询算法。该算法应用于树形结构中,解决节点间路径染色及颜色段计数问题。通过线段树维护路径信息,实现高效查询与更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

1、将节点a到节点b路径上所有点都染成颜色c;

2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“ 112221 ” 由3段组成:11 、222 和1 。

区间更新,区间查询。

很容易写错,开始cnt忘记清零找了一小时。r不小心写成l又找半小时。。。

一定要细心,这种题出错很难找。

#include <algorithm>
#include <string.h>
#include <stdio.h>
using namespace std;
#define maxn 300000

int n,m,dep[maxn],top[maxn],size[maxn],son[maxn],fa[maxn],id[maxn],val[maxn],val_pre[maxn];
struct node{
	int u,v,next;
}e[maxn];
int head[maxn],topw,q,num;
void addedge(int x,int y)
{
	e[num].u=x;e[num].v=y;e[num].next=head[x];head[x]=num++;
}
void dfs_1(int u,int f,int d)
{
	size[u]=1;
	dep[u]=d;
	fa[u]=f;
	son[u]=0;
	for(int i=head[u];~i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==f) continue;
		dfs_1(v,u,d+1);
		size[u]+=size[v];
		if(size[son[u]]<size[v]) son[u]=v;
	}
}
void dfs_2(int u,int tp)
{
	top[u]=tp;
	id[u]=++topw;
	if(son[u]) dfs_2(son[u],tp);
	for(int i=head[u];~i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==son[u]||v==fa[u]) continue;
		dfs_2(v,v);
	}
}


struct Tree{
	int l,r,lc,rc,cnt,lazy;
	Tree()
	{
		cnt=0;
	}
}tree[maxn];
void push_up(int rt)
{
	tree[rt].cnt=tree[rt<<1].cnt+tree[rt<<1|1].cnt;
	if(tree[rt<<1].rc==tree[rt<<1|1].lc) tree[rt].cnt--;
	tree[rt].lc=tree[rt<<1].lc;tree[rt].rc=tree[rt<<1|1].rc;
}
void push_down(int rt)
{
	if(tree[rt].lazy!=-1)
	{
		tree[rt<<1].lazy=tree[rt<<1].lc=tree[rt<<1].rc=tree[rt].lazy;tree[rt<<1].cnt=1;
		tree[rt<<1|1].lazy=tree[rt<<1|1].lc=tree[rt<<1|1].rc=tree[rt].lazy;tree[rt<<1|1].cnt=1;
		tree[rt].lazy=-1;
	}
}

void build(int l,int r,int rt)
{
	tree[rt].l=l;tree[rt].r=r;
	tree[rt].lazy=-1;
	if(l==r)
	{
		tree[rt].cnt=1;tree[rt].lc=tree[rt].rc=val[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,rt<<1);
	build(mid+1,r,rt<<1|1);
	push_up(rt);
}
void adde(int l,int r,int val,int rt)
{
	if(tree[rt].l>=l&&tree[rt].r<=r)
	{
		tree[rt].cnt=1;
		tree[rt].lazy=tree[rt].lc=tree[rt].rc=val;
		return ;
	}
	push_down(rt);
	int mid=(tree[rt].l+tree[rt].r)>>1;
	if(l<=mid) adde(l,r,val,rt<<1);
	if(r>mid) adde(l,r,val,rt<<1|1);
	push_up(rt);
}
void gao1(int u,int v,int val)
{
	int tp1=top[u],tp2=top[v];
	while(tp1!=tp2)
	{
		if(dep[tp1]<dep[tp2])
		{
			swap(tp1,tp2);
			swap(u,v);
		}
		adde(id[tp1],id[u],val,1);
		u=fa[tp1];
		tp1=top[u];
	}
	if(dep[u]>dep[v]) swap(u,v);
	adde(id[u],id[v],val,1);
}

Tree merge(Tree a,Tree b)
{
	Tree ans;
	if(a.cnt==0) return b;
	if(b.cnt==0) return a;
	ans.lc=a.lc;
	ans.rc=b.rc;
	ans.cnt=a.cnt+b.cnt;
	if(a.rc==b.lc) ans.cnt--;
	return ans;
}
Tree query(int l,int r,int rt)
{
	if(tree[rt].l>=l&&tree[rt].r<=r) return tree[rt];
	push_down(rt);
	Tree ans;
	int mid=(tree[rt].l+tree[rt].r)>>1;
	if(l<=mid) ans=merge(query(l,r,rt<<1),ans);
	if(r>mid) ans=merge(ans,query(l,r,rt<<1|1));
	push_up(rt);
	return ans;
}

int change(int u,int v)
{
	Tree ans1,ans2;
	int tp1=top[u],tp2=top[v];
	
	while(tp1!=tp2)
	{
		if(dep[tp1]<dep[tp2])
		{
			swap(ans1,ans2);swap(u,v);swap(tp1,tp2);
		}
		ans1=merge(query(id[tp1],id[u],1),ans1);
		u=fa[tp1];tp1=top[u];
	}
	if(dep[u]>dep[v])
	{
		swap(u,v);swap(ans1,ans2);
	}
	ans2=merge(query(id[u],id[v],1),ans2);
	if(ans1.cnt==0) return ans2.cnt;
	if(ans2.cnt==0) return ans1.cnt;
	int ans=ans1.cnt+ans2.cnt;
	if(ans1.lc==ans2.lc) ans--;
	return ans;
}
int main()
{
	while(scanf("%d%d",&n,&q)!=EOF)
	{
		memset(head,-1,sizeof(head));
		topw=0;num=0;
		for(int i=1;i<=n;i++)scanf("%d",&val_pre[i]);
		for(int i=1;i<n;i++)
		{
			int x,y;scanf("%d%d",&x,&y);
			addedge(x,y);
			addedge(y,x);
		}
		dfs_1(1,0,1);
		dfs_2(1,1);
		for(int i=1;i<=n;i++)
		{
			val[id[i]]=val_pre[i];
		}
		build(1,topw,1);
		//for(int i=1;i<=n;i++) printf("%d\n",tree[i].cnt);
		while(q--)
		{
			char s[10];
			scanf("%s",s);
			if(s[0]=='Q')
			{
				int x,y;scanf("%d%d",&x,&y);
				printf("%d\n",change(x,y));
			}
			else
			{
				int x,y,z;
				scanf("%d%d%d",&x,&y,&z);
				gao1(x,y,z);
			}
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值