P7735 [NOI2021] 轻重边
洛谷 [ ] 轻重边
题目大意
有一棵 个结点的树,树上的每一条边可能是轻边或者重边。接下来你需要对树进行 次操作,在所有操作开始前,树上所有边都是轻边。操作有以下两种:
- 给定两个点 和 ,首先对于 到 路径上的所有点 (包含 和 ),你要将与 相连的所有边变为轻边。然后再将 到 路径上包含的所有边变为重边。
- 给定两个点 和 ,你需要计算当前 到 的路径上一共包含多少条重边。
解题思路
前置知识:树链剖分。
首先思考如何只修改路径上点 的值,就可以将与 相连的所有边变为轻边,然后再将 到 路径上包含的所有边变为重边。
其实是可以的,我们可以将修改操作转化为将所有点 染成相同的颜色,如果相连的两个点颜色相等,即为重边,反之即为轻边。
没有太懂的话,具体的讲一讲,因为要将一条路径上的边修成重边,那么路径点染同色的操作就可以保证这条路径上点同色且所有边两端点同色,这样这些边就符合重边的判定了!而且更妙的是,由于这些点被染成的是一种独一无二的颜色,是一定不与前面的颜色重复的,所以所有染色点连边的另一端一定与它的颜色不同,这样子这些边就自然而然的变成了轻边。这个转化不得不说是很精妙的。
查询操作则转化为查询路径中同色相邻点对的数量。
我们再用变量
表示当前要往上跳的路径上次的终点颜色。
表示另一条路径上一次的终点颜色。
这样,如果当前往上跳的路径这次的起点颜色等于当前要往上跳的路径上次的终点颜色,那么颜色段数量加 。
如果当前要往上跳的节点所在路径发生了改变(也就是路径发生了交替),则
swap(p1,p2)
。
然后问题就落到如何找起点终点颜色了。
很简单,起点颜色就是线段树上查询的左端点的颜色,终点颜色就是查询的右端点的颜色。
在往上跳(线段树查询)的时候顺便记录一下 和 即可。
AC CODE
由于笔者快读太长,所以不便放在这里,请读者自行加入快读。
#include <bits/stdc++.h>
#define _ 1000002
#define lson o << 1
#define rson o << 1 | 1
using namespace std;
int T, n, m, p1, p2, lc, rc;
char ch;
int cnt;
array<int, _> head;
struct Edge
{
int to, nxt;
};
array<Edge, _ << 1> e;
struct Tree
{
int l, r, lc, rc, tag, val;
};
array<Tree, _ << 2> tr;
int res;
array<int, _> fa, dfn, siz, dep, hson, top;
inline void add(int u, int v)
{
e[++cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
inline void dfs1(int u, int d = 1)
{
dep[u] = d;
siz[u] = 1;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (dep[v])
continue;
fa[v] = u;
dfs1(v, d + 1);
siz[u] += siz[v];
if (siz[v] > siz[hson[u]])
hson[u] = v;
}
}
inline void dfs2(int u, int topf)
{
top[u] = topf;
dfn[u] = ++res;
if (!hson[u])
return;
dfs2(hson[u], topf);
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (top[v])
continue;
dfs2(v, v);
}
}
inline void push_up(int o)
{
tr[o].val = tr[lson].val + tr[rson].val;
if (tr[lson].rc == tr[rson].lc)
++tr[o].val;
tr[o].lc = tr[lson].lc;
tr[o].rc = tr[rson].rc;
}
inline void pushcol(int o, int a)
{
tr[o].lc = tr[o].rc = a;
tr[o].val = 1;
tr[o].tag = a;
}
inline void push_down(int o)
{
if (tr[o].tag)
{
if (lson)
pushcol(lson, tr[o].tag);
if (rson)
pushcol(rson, tr[o].tag);
int mid = (tr[o].l + tr[o].r) >> 1;
tr[lson].val = (mid - tr[o].l + 1 - 1);
tr[rson].val = (tr[o].r - mid - 1);
tr[o].tag = 0;
}
}
inline void build(int o, int l, int r)
{
tr[o].l = l;
tr[o].r = r;
if (l == r)
{
tr[o].val = 1;
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
push_up(o);
}
inline void update(int o, int L, int R, int val)
{
int l = tr[o].l, r = tr[o].r;
if (L <= l && r <= R)
{
pushcol(o, val);
tr[o].val = tr[o].r - tr[o].l;
return;
}
push_down(o);
int mid = (l + r) >> 1;
if (L <= mid)
update(lson, L, R, val);
if (R > mid)
update(rson, L, R, val);
push_up(o);
}
inline int query(int o, int L, int R)
{
int l = tr[o].l, r = tr[o].r;
if (L <= l && r <= R)
{
if (L == l)
lc = tr[o].lc;
if (R == r)
rc = tr[o].rc;
return tr[o].val;
}
push_down(o);
int mid = (l + r) >> 1;
int res = 0;
if (R <= mid)
return query(lson, L, R);
if (L > mid)
return query(rson, L, R);
res = query(lson, L, R) + query(rson, L, R);
if (tr[lson].rc == tr[rson].lc)
++res;
return res;
}
inline void add(int u, int v, int val)
{
while (top[u] != top[v])
{
if (dep[top[u]] < dep[top[v]])
swap(u, v);
update(1, dfn[top[u]], dfn[u], val);
u = fa[top[u]];
}
if (dfn[u] > dfn[v])
swap(u, v);
update(1, dfn[u], dfn[v], val);
}
inline int ask(int u, int v)
{
int res = 0;
p1 = p2 = 0;
while (top[u] != top[v])
{
if (dep[top[u]] < dep[top[v]])
swap(u, v), swap(p1, p2);
res += query(1, dfn[top[u]], dfn[u]);
if (rc == p1)
++res;
p1 = lc;
u = fa[top[u]];
}
if (dfn[u] > dfn[v])
swap(u, v), swap(p1, p2);
res += query(1, dfn[u], dfn[v]);
if (lc == p1)
++res;
if (rc == p2)
++res;
return res;
}
inline void init()
{
cnt = 0;
head.fill(0);
e.fill({0, 0});
tr.fill({0, 0, 0, 0, 0, 0});
res = 0;
fa.fill(0);
dfn.fill(0);
siz.fill(0);
dep.fill(0);
hson.fill(0);
top.fill(0);
}
/*
void check(int o=1,int l=1,int r=n)
{
if(l==r)
{
printf("%d %d %d %d %d\n",tr[o].l,tr[o].val,tr[o].lc,tr[o].rc,tr[o].tag);
return;
}
int mid=(l+r)>>1;
check(lson,l,mid);
check(rson,mid+1,r);
}
*/
signed main()
{
cin >> T;
while (T--)
{
init();
cin >> n >> m;
for (int i = 1; i < n; ++i)
{
int u, v;
cin >> u >> v;
add(u, v);
add(v, u);
}
build(1, 1, n);
dfs1(1);
dfs2(1, 1);
for (int i = 1; i <= n; ++i)
update(1, dfn[i], dfn[i], -dfn[i]);
for (int i = 1; i <= m; ++i)
{
int op, a, b;
cin >> op >> a >> b;
if (op == 1)
add(a, b, i);
else
cout << ask(a, b) << endl;
}
}
return 0;
}
本文来自博客园,作者:蒟蒻orz,转载请注明原文链接:https://www.cnblogs.com/orzz/p/18122153
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!