轻重边:小清新树剖题。

思路

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

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

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

时间复杂度 \(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. 2023-05 多校联合训练 ZJNU站 正式赛

    Scarlett的三元组 有一个长度为 \(n\) 的序列 \(a_1,a_2,\cdots,a_n\),试问有多少个三元组 \((a_i,a_j,a_k)\) 满足: \(1 \le i \lt j ...

  2. AtCoder Beginner Contest 296

    Transition Game 给定序列\(a\),\(1<=a_i<=n\),一场游戏有\(n\)个回合,第\(i\)回合时,第一个人先指定一个任意数\(k\),第二个人任意选定一个\( ...

  3. uniapp云数据库笔记

    1.基本概念 云数据库:一个云空间只能有一个数据库,一个数据库可以有多个集合(表),每个表可以有多行数据(文档) DB Schema:是基于 JSON 格式定义的数据结构的规范,每个表有多少字段都需要 ...

  4. Sortable.js笔记

    1.前言 SortableJS是功能强大的JavaScript 拖拽库,更多配置项:Sortable.js中文网|配置 引入插件 <script src="https://cdn.bo ...

  5. uni.showModel内容换行

    前情 最近在做小程序项目,选用有是当前比较火的uniapp技术栈,在产品项目中用到不少的需要引导用户确认后才继续操作的弹框. 为什么想到我去换行? 其实showModel弹框的content是支持自动 ...

  6. openEuler-怎么看服务器操作系统是不是欧拉系统?

    ​[root@localhost ~]# cat /etc/os-release NAME="openEuler" VERSION="22.03 (LTS-SP2)&qu ...

  7. IOS CABasicAnimation实现旋转动画

    IOS CABasicAnimation实现旋转动画 定义一个CABasicAnimation lazy var rotateAnimation: CABasicAnimation = { let a ...

  8. 使用PG的部分索引

    PG 又带来一个惊喜. 现在有一张表,每天增加几十万数据,数据量迅速超过 1亿.此时 create_at 上的索引已经非常庞大,检索速度很慢. 接下来要分表分区了? NO,PG 有一个非常有意思的特性 ...

  9. java判断反射的Field是不是用final修饰

    今天调用Field.set方法报错: Caused by: java.lang.IllegalAccessException: Can not set static final long field ...

  10. 龙哥量化:通达信财富币不够怎么办:K线训练营100%胜率,赚财富币

    通达信app的K线训练营中,[K线训练]和[K线对战]都需要花费[5财富币]进行训练,[K线对战]胜利的话可以获得10财富币.注意:是对战,对战,对战,那怎么才能每场都胜呢,哈哈,我们找到历史K线,对 ...