Luogu P7735 NOI2021 轻重边 题解 [ 紫 ] [ 树链剖分 ] [ 线段树 ]
轻重边:小清新树剖题。
思路
我们可以给每一个赋重边的操作看做给这些点盖上一个时间戳,那么显然一条边是重边,当且仅当这条边两端的点的时间戳相等。因为一个点如果被后面的时间戳覆盖之后他相邻的边都会被波及,之前的时间戳也就无效了,直接变为了轻边;并且只要相邻的两个点被覆盖的时间不同,那么这一定不是重边。
于是,我们在线段树上维护每个点的时间戳的同时,再维护相邻的且相等的时间戳即可。
这里可以参考染色那题的线段树,维护颜色段个数,然后用链的长度减掉颜色段个数就是答案。也可以直接维护相邻的且相等的时间戳个数。
时间复杂度
感觉这题想到每次修改操作盖时间戳这一点才是关键。
代码
#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;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战