Luogu P7735 NOI2021 轻重边 题解 [ 紫 ] [ 树链剖分 ] [ 线段树 ]

轻重边:小清新树剖题。

思路

我们可以给每一个赋重边的操作看做给这些点盖上一个时间戳,那么显然一条边是重边,当且仅当这条边两端的点的时间戳相等。因为一个点如果被后面的时间戳覆盖之后他相邻的边都会被波及,之前的时间戳也就无效了,直接变为了轻边;并且只要相邻的两个点被覆盖的时间不同,那么这一定不是重边。

于是,我们在线段树上维护每个点的时间戳的同时,再维护相邻的且相等的时间戳即可。

这里可以参考染色那题的线段树,维护颜色段个数,然后用链的长度减掉颜色段个数就是答案。也可以直接维护相邻的且相等的时间戳个数。

时间复杂度 O(nlog2n)

感觉这题想到每次修改操作盖时间戳这一点才是关键。

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
typedef long long ll;
using namespace std;
using pi=pair<int,int>;
const int N=100005;
int n,m,w[N],fa[N],dep[N],son[N],sz[N],top[N],id[N],cnt=0,a[N];
vector<int>g[N];
void dfs1(int u,int f)
{
	fa[u]=f;sz[u]=1;dep[u]=dep[f]+1;
	for(auto v:g[u])
	{
		if(v==f)continue;
		dfs1(v,u);
		sz[u]+=sz[v];
		if(sz[son[u]]<sz[v])son[u]=v;
	}
}
void dfs2(int u,int tp)
{
	top[u]=tp;id[u]=++cnt;a[cnt]=w[u];
	if(son[u]==0)return;
	dfs2(son[u],tp);
	for(auto v:g[u])
	{
		if(v==fa[u]||v==son[u])continue;
		dfs2(v,v);
	}
}
struct node{
	int l,r,v,lx,rx,tag;
};
struct segtree{
	node tr[4*N];
	void pushup(node &p,node ls,node rs)
	{
		p.v=ls.v+rs.v-(ls.rx==rs.lx);
		p.lx=ls.lx;
		p.rx=rs.rx;
	}
	void pushdown(int p)
	{
		if(tr[p].tag)
		{
			tr[lc].tag=tr[p].tag;
			tr[rc].tag=tr[p].tag;
			tr[lc].lx=tr[p].tag;
			tr[rc].lx=tr[p].tag;
			tr[lc].rx=tr[p].tag;
			tr[rc].rx=tr[p].tag;
			tr[lc].v=1;
			tr[rc].v=1;
		}
		tr[p].tag=0;
	}
	void build(int p,int ln,int rn)
	{
		tr[p]={ln,rn,1,a[ln],a[ln],0};
		if(ln==rn)return;
		int mid=(ln+rn)>>1;
		build(lc,ln,mid);
		build(rc,mid+1,rn);
		pushup(tr[p],tr[lc],tr[rc]);
	}
	void update(int p,int ln,int rn,int k)
	{
		if(ln<=tr[p].l&&tr[p].r<=rn)
		{
			tr[p].v=1;
			tr[p].lx=k;
			tr[p].rx=k;
			tr[p].tag=k;
			return;
		}
		pushdown(p);
		int mid=(tr[p].l+tr[p].r)>>1;
		if(ln<=mid)update(lc,ln,rn,k);
		if(rn>=mid+1)update(rc,ln,rn,k);
		pushup(tr[p],tr[lc],tr[rc]);
	}
	node query(int p,int ln,int rn)
	{
		if(ln<=tr[p].l&&tr[p].r<=rn)return tr[p];
		pushdown(p);
		int mid=(tr[p].l+tr[p].r)>>1;
		if(rn<=mid)return query(lc,ln,rn);
		if(ln>=mid+1)return query(rc,ln,rn);
		node tmp;
		pushup(tmp,query(lc,ln,rn),query(rc,ln,rn));
		return tmp;
	}
	void update_path(int u,int v,int k)
	{
		while(top[u]!=top[v])
		{
			if(dep[top[u]]<dep[top[v]])swap(u,v);
			update(1,id[top[u]],id[u],k);
			u=fa[top[u]];
		}
		if(dep[u]<dep[v])swap(u,v);
		update(1,id[v],id[u],k);
	}
	pi query_path(int u,int v)
	{
		node resu={0,0,0,0,0,0},resv={0,0,0,0,0,0};
		while(top[u]!=top[v])
		{
			if(dep[top[u]]<dep[top[v]])swap(u,v),swap(resu,resv);
			pushup(resu,query(1,id[top[u]],id[u]),resu);
			u=fa[top[u]];
		}
		if(dep[u]<dep[v])swap(u,v),swap(resu,resv);
		pushup(resu,query(1,id[v],id[u]),resu);
		return {(resu.v+resv.v-(resu.lx==resv.lx)),v};
	}
}tr1;
void solve()
{
	cin>>n>>m;
	memset(son,0,sizeof(son));
	for(int i=0;i<=n;i++)
	{
		g[i].clear();
	}
	for(int i=1;i<n;i++)
	{
		int u,v;
		cin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	cnt=0;
	dfs1(1,0);
	dfs2(1,1);
	tr1.build(1,1,cnt);
	for(int i=1;i<=n;i++)tr1.update(1,id[i],id[i],-i);
	for(int i=1;i<=m;i++)
	{
		int op,a,b;
		cin>>op>>a>>b;
		if(op==1)
		{
			tr1.update_path(a,b,i);
		}
		else
		{
			pi res=tr1.query_path(a,b);
			cout<<dep[a]+dep[b]-2*dep[res.se]-res.fi+1<<'\n';
		}
	}
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin>>t;
	while(t--)solve();
	return 0;
}
posted @   KS_Fszha  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示