轻重边:小清新树剖题。

思路

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

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

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

时间复杂度 \(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之类型转换cast

    Go 语言作为强类型语言,在使用 Golang 开发项目时,经常会遇到类型转换的场景,整型之间可以直接转换,字节切片和字符串之间也可以直接转换. 但是,如果整型和字符串之间做类型转换,则需要使用 st ...

  2. 子组件监听props中的值,监听不到旧值的相关问题

    昨天,在项目中做一个功能,一个tab切换,点击其中一个tab的时候,调用组件中的查询方法,切只调用一次.再次切换的时候不再调用. 我的做法是: 在父组件中创建一个变量,初始化data中设为0,在点击t ...

  3. Mac下常用软件汇总(精)

    1.连接Windows远程连接工具(Microsoft-Remote-Desktop-For-Mac ) 2.SSH管理工具:Royal TSX 下载地址:Royal Apps Royal TSX 是 ...

  4. Jenkins篇-权限管理

    我使用 权限管理插件是Role Strategy Plugin,他可以对构建的项目进行授权管理,让不同的用户管理不同的项目,将不同环境的权限进行区分 1)安装插件 系统管理>插件管理查找Role ...

  5. Node.js 文件读写

    1.fs模块 在node.js中,所有文件的操作都是通过fs模块来实现的.包括文件目录的创建,删除,查询以及文件的读取,写入. 在fs模块中,所有的方法都分成同步和异步两种实现,具有sync后缀的为同 ...

  6. orangepi zero3 使用dd命令进行SD卡系统备份与还原

    1. 使用dd命令备份整个sd卡 首先使用 df -h命令查看sd卡挂载名,如下所示,sd卡挂载为 /dev/sdc meng@meng:~/桌面/code$ df -h 文件系统 大小 已用 可用 ...

  7. merging rhino 哈哈

  8. [sa-token]StpUtil.getLoginId

    闲聊 一般情况下,我们想用uid,可能需要前端将uid传过来,或者将token传来,然后我们进行识别. 用了sa-token之后,可以使用StpUtil.getLoginId()方法获取当前会话的用户 ...

  9. 【MyBatis】学习笔记04:配置文件模板

    [Mybatis]学习笔记01:连接数据库,实现增删改 [Mybatis]学习笔记02:实现简单的查 [MyBatis]学习笔记03:配置文件进一步解读(非常重要) 目录 IDEA配置模板的地方 核心 ...

  10. gitlab-runner register

    [root@g ~]# gitlab-runner register Runtime platform arch=amd64 os=linux pid=23614 revision=ac8e767a ...