轻重边:小清新树剖题。

思路

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

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

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

时间复杂度 \(O(n\log^2 n)\)。

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

代码

#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;
}

Luogu P7735 NOI2021 轻重边 题解 [ 紫 ] [ 树链剖分 ] [ 线段树 ]的更多相关文章

  1. 洛谷P3313 [SDOI2014]旅行 题解 树链剖分+线段树动态开点

    题目链接:https://www.luogu.org/problem/P3313 这道题目就是树链剖分+线段树动态开点. 然后做这道题目之前我们先来看一道不考虑树链剖分之后完全相同的线段树动态开点的题 ...

  2. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

  3. Aizu 2450 Do use segment tree 树链剖分+线段树

    Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show ...

  4. 洛谷P4092 [HEOI2016/TJOI2016]树 并查集/树链剖分+线段树

    正解:并查集/树链剖分+线段树 解题报告: 传送门 感觉并查集的那个方法挺妙的,,,刚好又要复习下树剖了,所以就写个题解好了QwQ 首先说下并查集的方法趴QwQ 首先离线,读入所有操作,然后dfs遍历 ...

  5. POJ3237 Tree 树链剖分 线段树

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - POJ3237 题意概括 Description 给你由N个结点组成的树.树的节点被编号为1到N,边被编号为1 ...

  6. 【CF725G】Messages on a Tree 树链剖分+线段树

    [CF725G]Messages on a Tree 题意:给你一棵n+1个节点的树,0号节点是树根,在编号为1到n的节点上各有一只跳蚤,0号节点是跳蚤国王.现在一些跳蚤要给跳蚤国王发信息.具体的信息 ...

  7. 【bzoj5210】最大连通子块和 树链剖分+线段树+可删除堆维护树形动态dp

    题目描述 给出一棵n个点.以1为根的有根树,点有点权.要求支持如下两种操作: M x y:将点x的点权改为y: Q x:求以x为根的子树的最大连通子块和. 其中,一棵子树的最大连通子块和指的是:该子树 ...

  8. 【bzoj4712】洪水 树链剖分+线段树维护树形动态dp

    题目描述 给出一棵树,点有点权.多次增加某个点的点权,并在某一棵子树中询问:选出若干个节点,使得每个叶子节点到根节点的路径上至少有一个节点被选择,求选出的点的点权和的最小值. 输入 输入文件第一行包含 ...

  9. 2243: [SDOI2011]染色 树链剖分+线段树染色

    给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段), 如“112221”由3段组 ...

  10. 【bzoj2402】陶陶的难题II 分数规划+树链剖分+线段树+STL-vector+凸包+二分

    题目描述 输入 第一行包含一个正整数N,表示树中结点的个数.第二行包含N个正实数,第i个数表示xi (1<=xi<=10^5).第三行包含N个正实数,第i个数表示yi (1<=yi& ...

随机推荐

  1. Golang基础库之net/http

    Go语言内置的net/http包十分的优秀,提供了HTTP客户端和服务端的实现. net/http介绍 Go语言内置的net/http包提供了HTTP客户端和服务端的实现. HTTP协议 超文本传输协 ...

  2. 理解Flink之三Transformation

    Transformation 是 Flink操作的底层实现,无论是map还是Flatmap. DataStream类中包含两个变量: StreamExecutionEnvironment Transf ...

  3. http相关知识要点

    1.TCP/IP协议分为哪几层?每一层主要作用是什么?为什么要分层? 应用层 传输层 网络层 数据链路层 2.HTTP请求有哪些方式? GET:用于从服务器获取资源.不会改变资源状态,无副作用,幂等. ...

  4. 【Amadeus原创】Docker容器的备份与还原

    主要作用: 就是让配置好的容器,可以得到复用,后面用到得的时候就不需要重新配置. 其中涉及到的命令有: docker commit 将容器保存为镜像 docker save -o 将镜像备份为tar文 ...

  5. gitlab runner install

    gitlab-runner install -d /home/gitlab-runner/ --syslog --user gitlab-runner

  6. Qt开发经验小技巧231-235

    关于c++中继承多态virtual和override的几点总结. 子类可以直接使用基类中的protected下的变量和函数. 基类函数没加virtual,子类有相同函数,实现的是覆盖.用基类指针调用时 ...

  7. 开源即时通讯IM框架MobileIMSDK的H5端技术概览

    一.基本介绍 MobileIMSDK的H5端是一套纯JS编写的基于标准WebSocket的即时通讯库: 1)超轻量级.极少依赖: 2)纯JS编写.高度提炼,简单易用: 3)基于标准WebSocket协 ...

  8. IM跨平台技术学习(十三):从理论到实践,详细对比Electron和Tauri的优劣

    本文由京东技术王泽知分享,原题"基于Web的跨平台桌面应用开发",下文进行了排版和内容优化. 1.引言 近些年来,跨平台跨端一直是比较热门的话题,Write once, run a ...

  9. IM通讯协议专题学习(二):快速理解Protobuf的背景、原理、使用、优缺点

    本文由vivo技术团队Li Guanyun分享,为了提升阅读体验,进行了较多修订和重新排版. 1.引言 Protobuf 作为一种跨平台.语言无关.可扩展的序列化结构数据通讯协议,已广泛应用于网络数据 ...

  10. Appium_iOS 配置

    一. iOS Driver 配置 options = AppiumOptions()options.load_capabilities({ "platformName": &quo ...