凤凰彩票官网首页 - Welcome

  • 凤凰彩票_凤凰彩首页 2026-06-13: 查询树上回环旅途。用go言语, 给定一棵包含 n 个节点

  • 发布日期:2026-06-13 21:31    点击次数:117

凤凰彩票_凤凰彩首页 2026-06-13: 查询树上回环旅途。用go言语, 给定一棵包含 n 个节点

2026-06-13:查询树上回环旅途。用go言语,给定一棵包含 n 个节点的无向树(节点编号为 0 到 n-1),树用数组 edges 暗意:edges 的长度为 n-1,每个元素为 [u, v],暗意节点 u 与 v 之间有一条无向边。

另外给定一个字符串 s(长度为 n,只包含小写英翰墨母),其中 s[i] 是分派给节点 i 的字符。

再给定多少操作 queries,每条操作有两种身手之一:

• update u c:把节点 u 的字符更新为 c,即令 s[u] = c。

• query u v:考虑从节点 u 到节点 v 的独一谈径(包含 u 和 v 这两个端点)。把这条旅途上

• 整个节点对应的字符顺次取出,组成一个字符串。判断这个字符串的字符能否通过重排得到一个回环串(回环串指正着读和反着读疏通)。

需要为每条 query 驱逐复返一个布尔值:能重排成回环串则为 true,不然为 false。复返驱逐按 queries 中出现的 query 限定组成数组。

1

edges.length == n - 1。

edges[i] = [ui, vi]。

0

s 由小写英翰墨母组成。

输入生成的 edges 暗意一棵灵验的树。

1

queries[i] = "update ui c" 或

queries[i] = "query ui vi"。

0

c 是一个小写英翰墨母。

输入: n = 3, edges = [[0,1],[1,2]], s = "aac", queries = ["query 0 2","update 1 b","query 0 2"]。

输出: [true,false]。

解说:

"query 0 2":旅途 0 → 1 → 2 得到的字符串是 "aac",不错重新罗列酿成 "aca",这是一个回环串。因此,answer[0] = true。

"update 1 b":将节点 1 更新为 'b',当今 s = "abc"。

"query 0 2":旅途上的字符为 "abc",无法重新罗列酿成回环串。因此,answer[1] = false。

因此,answer = [true, false]。

题目来独力扣3841。

一、全体解题中枢旨趣(前置铺垫)

1. 字符串可重排成回环的判定例则

一个字符串打乱后能组成回环,充要条目:最多唯有1种字符出现奇数次,其余全部偶数次。

用二进制掩码简化统计:26个小写字母对应26位二进制数,第k位为1代表字母'a'+k出现次数为奇数,0代表偶数;异或运算恰好能已毕“次数奇偶翻转”。

• 旅途字符总奇偶掩码:二进制中1的数目 ≤ 1 → 输出true,不然false;

• 判断掩码1的个数:mask & (mask-1) == 0,仅0个/1个1时等式配置。

2. 树上两点旅途异或掩码数学公式

树上u到v的旅途 = 根→u + 根→v - 2×根→LCA(u,v) + LCA本人

异或性质:团结个数异或两次会对消(x^x=0),因此旅途原始静态掩码公式:

xor(u,v) = rootXor[u] ^ rootXor[v] ^ rootXor[lca] ^ rootXor[lca] ^ val(lca)

化简:xor(u,v) = rootXor[u] ^ rootXor[v] ^ val(lca)

3. 单点字符修改的难点与树状数组差分有缠绵

世俗静态前缀掩码只可贬责不变字符,本题有update单点修改:修改节点x字符,等价于x整棵子树内整个点的根前缀掩码全部异或一个修正好val。

原因:放荡节点y在x子树内,根到y的旅途一定会经过x;x字符改变,整个子树节点的根旅途奇偶掩码全部翻转。

DFS工夫戳将子树映射为贯穿区间[in[x], out[x]],区间异或更新用差分树状数组(异或版Fenwick) 已毕:

• 区间[l, r]异或val:update(l, val)、update(r+1, val);

• 查询单点in[x]的全局修正总掩码:树状数组前缀异或pre(in[x]);

• 最终信得过根前缀掩码 = 原始静态rootXor[x] ^ 全局修正掩码pre(in[x])。

4. LCA求解形态:二进制倍增

预贬责每个节点2^k级祖宗,O(logn)求两点最近大家祖宗,适配5e4限制数据。

二、代码完好分步实施历程(不含代码,纯逻辑拆解)

阶段1:输入数据脱手化与无向树建衔接表

1. 读取节点总和n、边数组edges、脱手字符串s、操作数组queries;

2. 构建大小为n的衔接表g:遍历每条边[u,v],双向添加(无向树),g[u]追加v、g[v]追加u;

3. 预分派全局存储数组:

• dep[]:每个节点深度;

• timeIn[]/timeOut[]:DFS入、出工夫戳,标记子树区间;

• pa[][]:倍增祖宗表,pa[x][k]代表节点x进取跳2^k步的祖宗;

• pathXorFromRoot[]:静态根前缀异或掩码,未修改字符时的原始奇偶掩码;

• t[]:字节数组,及时保存每个节点刻下字符,反应update修改;

• clock:工夫戳计数器,脱手0。

阶段2:DFS遍历,完成工夫戳、深度、静态掩码、一级祖宗预贬责

根固定为节点0,递归DFS(x, 父节点p):

1. 记载x的2^0级祖宗pa[x][0] = p;

2. 工夫戳clock自增,赋值timeIn[x] = clock;

3. 计较静态根掩码pathXorFromRoot[x]:父节点静态掩码 异或 刻下节点字符对应二进制位;根点0单独脱手化掩码为自身字符;

4. 遍历x整个衔接子节点y,跳过父节点p:

• 子节点深度dep[y] = dep[x]+1;

• 递归实施DFS(y, x);

5. 整个子节点遍历完成后,赋值timeOut[x] = clock;此时x的整棵子树对应工夫戳贯穿区间[timeIn[x], timeOut[x]]。

阶段3:预贬责二进制倍增祖宗表(完好2^k级祖宗)

1. 计较最大倍增层数mx:取n二进制位数,保证2^mx掩饰树最大深度;

2. 逐层递推填充pa数组:从k=0到mx-2,遍历整个节点x:

• 若x的2^k级祖宗p≠-1,则x的2^(k+1)级祖宗 = p的2^k级祖宗;

• 若p=-1(无表层祖宗),则pa[x][k+1] = -1;

完成后可通过倍增快速进取跳放荡步数。

阶段4:封装两个器具函数

器具1:uptoDep(x, d)

将节点x进取跳,直到深度就是d:

1. 计较需要进取跳的步数差值k = dep[x]-d;

2. 轮回取出k最低位2^t,凤凰彩票_凤凰彩首页x跳至pa[x][t],排除最低位,直到k=0;复返调整深度后的节点。

器具2:getLCA(x, y),求两点最近大家祖宗

1. 保证y深度更深,若dep[x]>dep[y]则交换x、y;

2. 调用uptoDep把y上跳到与x团结深度;

3. 若x==y,径直复返x为LCA;

4. 从最大倍增层向下遍历到0:若x、y刻下2^k祖宗不疏通,同步将x、y跳至各自祖宗;

5. 轮回驱逐后x、y父节点即为LCA,复返pa[x][0]。

阶段5:脱手化异或型树状数组Fenwick

1. 树状数组长度n+1(工夫戳从1脱手,下标1~n);

2. 树状数组重载异或更新、前缀异或查询逻辑,2026年世界杯中国官网替代世俗加法;

3. 作用:治愈整个update操作带来的区间异或修正,单点查询得到该节点累计修正掩码。

阶段6:逐行贬责每一条查询操作,分两类分支

分支A:操作是 update u c(修改节点u字符为c)

1. 索取缠绵节点编号u、新字符c,读取节点u刻下旧字符t[u];

2. 计较修正掩码val:旧字符二进制位 异或 新字符二进制位;val代表u子树整个节点掩码需要翻转的数值;

3. 更新全局字符数组t[u] = c,保存最新字符;

4. 春联树区间[timeIn[u], timeOut[u]]实施区间异或更新:

• 树状数组更新 timeIn[u] 位置,异或val;

• 树状数组更新 timeOut[u]+1 位置,异或val;

差分旨趣:区间内前缀查询会重叠val,区间外对消为0。

分支B:操作是 query x y(查询x到y旅途能否重排回环)

1. 索取两个端点x、y;调用getLCA得到两点大家祖宗lca;

2. 差异查询x、y工夫戳对应的累计修正掩码:fx = f.pre(timeIn[x]),fy = f.pre(timeIn[y]);

3. 计较两点及时信得过根前缀掩码:

realX = pathXorFromRoot[x] ^ fx

realY = pathXorFromRoot[y] ^ fy

4. 套旅途掩码公式计较整条旅途总奇偶掩码:

totalMask = realX ^ realY ^ (1

推导阐明:realX^realY对消根到LCA两段旅途,清寒LCA自身字符,需单独补上;

5. 判断回环条目:totalMask二进制中1的数目≤1,等价于totalMask & (totalMask-1) == 0;

6. 将布尔驱逐存入谜底数组ans。

阶段7:整个操作贬责结束,复返ans数组手脚最终输出

三、样例输入完好推演(提拔分解历程)

输入:n=3,边0-1、1-2,脱手s=aac,查询3条

1. DFS后工夫戳:in[0]=1,in[1]=2,in[2]=3;out[0]=3,out[1]=3,out[2]=3;

静态掩码:pathXor[0]=001(a)、pathXor[1]=001^010(a^a)=000、pathXor[2]=000^100(c)=100;

2. 第一条query 0 2:

LCA=1;无update修正fx=fy=0;

totalMask=001 ^ 100 ^ 010(lca字符a)=111;二进制两个1,忻悦≤1 → true;

3. update 1 b:旧字符a(001),新b(010),val=011;区间[2,3]异或011;

4. 第二条query 0 2:

fx=pre(1)=0,fy=pre(3)=011;

realX=001^0=001,realY=100^011=111;

totalMask=001^111 ^ 010(lca当今是b)=100;二进制3个1 → false;

输出 [true,false],与样例匹配。

四、总工夫复杂度分析

设节点数目n,查询操作数目q(n、q上限均5e4),倍增层数log₂n≈16。

1. 预贬责阶段复杂度

1. 建衔接表:O(n)

2. DFS遍历整棵树:O(n)

3. 倍增祖宗表填充:O(n·logn)

总预贬责:O(n logn)

2. 每条操作复杂度

• update操作:两次树状数组更新,单次Fenwick O(logn) → O(logn)

• query操作:1次LCA(O(logn))+ 两次Fenwick前缀查询(各O(logn))+ 浅易元运算 → O(logn)

全部q次操作总复杂度:O(q logn)

全体总工夫复杂度

O((n + q) · logn)

n、q均5e4,logn≈16,总运算量可控,忻悦数据鸿沟要求。

五、总非凡空间复杂度分析

1. 衔接表g:存储n-1条双向边 → O(n)

2. dep、timeIn、timeOut、pathXorFromRoot、t数组:均长度n → O(n)

3. 倍增祖宗表pa:n × logn(logn固定16) → O(n logn)

4. 异或树状数组fenwick:长度n+1 → O(n)

5. 谜底数组ans:最多q个布尔值 → O(q)

总非凡空间复杂度

O(n logn + q)

主导项为倍增表n logn,q与n同量级,实质可简化描述为O(n logn)。

Go完好代码如下:

package main

import (

"fmt"

"math/bits"

"strconv"

"strings"

)

type fenwick []int

func newFenwickTree(n int) fenwick {

returnmake(fenwick, n+1) // 使用下标 1 到 n

}

// a[i] ^= val

// 1

// 工夫复杂度 O(log n)

func (f fenwick) update(i int, val int) {

for ; i

f[i] ^= val

}

}

// 计较前缀异或和 a[1] ^ ... ^ a[i]

// 1

// 工夫复杂度 O(log n)

func (f fenwick) pre(i int) (res int) {

for ; i > 0; i &= i - 1 {

res ^= f[i]

}

return

}

func palindromePath(n int, edges [][]int, s string, queries []string) (ans []bool) {

g := make([][]int, n)

for _, e := range edges {

x, y := e[0], e[1]

g[x] = append(g[x], y)

g[y] = append(g[y], x)

}

mx := bits.Len(uint(n))

pa := make([][16]int, n)

dep := make([]int, n)

timeIn := make([]int, n) // DFS 工夫戳

timeOut := make([]int, n)

clock := 0

pathXorFromRoot := make([]int, n) // 从根脱手的旅途中的字母奇偶性的蛊惑

pathXorFromRoot[0] = 1

var dfs func(int, int)

dfs = func(x, p int) {

pa[x][0] = p

clock++

timeIn[x] = clock

for _, y := range g[x] {

if y != p {

dep[y] = dep[x] + 1

pathXorFromRoot[y] = pathXorFromRoot[x] ^ 1

dfs(y, x)

}

}

timeOut[x] = clock

}

dfs(0, -1)

for i := range mx - 1 {

for x := range pa {

p := pa[x][i]

if p != -1 {

pa[x][i+1] = pa[p][i]

} else {

pa[x][i+1] = -1

}

}

}

uptoDep := func(x, d int)int {

for k := uint32(dep[x] - d); k > 0; k &= k - 1 {

x = pa[x][bits.TrailingZeros32(k)]

}

return x

}

// 复返 x 和 y 的最近大家祖宗

getLCA := func(x, y int)int {

if dep[x] > dep[y] {

x, y = y, x

}

y = uptoDep(y, dep[x]) // 使 y 和 x 在团结深度

if y == x {

return x

}

for i := mx - 1; i >= 0; i-- {

px, py := pa[x][i], pa[y][i]

if px != py {

x, y = px, py // 同期往上跳 2^i 步

}

}

return pa[x][0]

}

// 上头全是模板,底下脱手本题逻辑

t := []byte(s)

f := newFenwickTree(n) // 预防树状数组是异或运算

for _, q := range queries {

if q[0] == 'u' {

x, _ := strconv.Atoi(q[7 : len(q)-2])

c := q[len(q)-1]

val := 1

t[x] = c

// 子树 x 全部异或 val,改换成对区间 [timeIn[x], timeOut[x]] 的差分更新

f.update(timeIn[x], val)

f.update(timeOut[x]+1, val)

} else {

q = q[6:]

i := strings.IndexByte(q, ' ')

x, _ := strconv.Atoi(q[:i])

y, _ := strconv.Atoi(q[i+1:])

lca := getLCA(x, y)

res := pathXorFromRoot[x] ^ pathXorFromRoot[y] ^ f.pre(timeIn[x]) ^ f.pre(timeIn[y]) ^ 1

ans = append(ans, res&(res-1) == 0) // 至多一个字母的出现次数是奇数

}

}

return

}

func main {

n := 3

edges := [][]int{{0, 1}, {1, 2}}

s := "aac"

queries := []string{"query 0 2", "update 1 b", "query 0 2"}

result := palindromePath(n, edges, s, queries)

fmt.Println(result)

}

Python完好代码如下:

# -*-coding:utf-8-*-

import sys

from typing import List

def palindromePath(n: int, edges: List[List[int]], s: str, queries: List[str]) -> List[bool]:

# 建图

g = [[] for _ in range(n)]

for x, y in edges:

g[x].append(y)

g[y].append(x)

# 二进制提高的层数

mx = n.bit_length

pa = [[-1] * mx for _ in range(n)]

dep = [0] * n

time_in = [0] * n

time_out = [0] * n

path_xor = [0] * n # 从根到刻下节点的旅途异或值

path_xor[0] = 1

clock = 0

# 递归可能会很深,提高递归深度限定

sys.setrecursionlimit(300000)

def dfs(x: int, p: int):

nonlocal clock

pa[x][0] = p

clock += 1

time_in[x] = clock

for y in g[x]:

if y != p:

dep[y] = dep[x] + 1

path_xor[y] = path_xor[x] ^ (1

dfs(y, x)

time_out[x] = clock

dfs(0, -1)

# 拓荒倍增表

for i in range(mx - 1):

for x in range(n):

p = pa[x][i]

if p != -1:

pa[x][i + 1] = pa[p][i]

else:

pa[x][i + 1] = -1

def upto_dep(x: int, d: int) -> int:

# 将节点 x 提高到深度 d

diff = dep[x] - d

博亚体育app2026世界杯中国官网下载

while diff > 0:

lsb = diff & -diff

step = (lsb.bit_length - 1) # 最低位 1 的位置

x = pa[x][step]

diff ^= lsb # diff -= lsb 也不错

return x

def get_lca(x: int, y: int) -> int:

if dep[x] > dep[y]:

x, y = y, x

y = upto_dep(y, dep[x]) # 提高到团结深度

if y == x:

return x

for i in range(mx - 1, -1, -1):

px, py = pa[x][i], pa[y][i]

if px != py:

x, y = px, py

return pa[x][0]

# 树状数组(异或版块)

class Fenwick:

def __init__(self, size: int):

self.tree = [0] * (size + 1) # 下标 1..size

def update(self, i: int, val: int):

while i

self.tree[i] ^= val

i += i & -i

def pre(self, i: int) -> int:

res = 0

while i > 0:

res ^= self.tree[i]

i &= i - 1

return res

t = list(s) # 可变字符串

bit = Fenwick(n) # 最多用到 time_out[x]+1,time_out 最大为 n,因此 n 迷漫(实质上需要 n+1 大小,用 n+2 更安全)

# 为安全起见,不错扩大少量

bit = Fenwick(n + 5)

ans = []

for q in queries:

if q.startswith('u'): # update

parts = q.split

x = int(parts[1])

c = parts[2]

old = t[x]

val = (1

t[x] = c

# 子树区间差分更新

bit.update(time_in[x], val)

bit.update(time_out[x] + 1, val)

else: # query

parts = q.split

x = int(parts[1])

y = int(parts[2])

lca = get_lca(x, y)

res = (path_xor[x] ^ path_xor[y] ^

bit.pre(time_in[x]) ^ bit.pre(time_in[y]) ^

(1

ans.append(res & (res - 1) == 0) # 至多一个字母出现奇数次

return ans

if __name__ == "__main__":

n = 3

edges = [[0, 1], [1, 2]]

s = "aac"

queries = ["query 0 2", "update 1 b", "query 0 2"]

result = palindromePath(n, edges, s, queries)

print(result)

C++完好代码如下:

#include

#include

#include

#include

#include

using namespace std;

// 树状数组(异或版块)

class Fenwick {

vector tree;

int n;

public:

Fenwick(int size) : n(size), tree(size + 1) {} // 下标 1..n

// a[i] ^= val, 1

void update(int i, int val) {

while (i

tree[i] ^= val;

i += i & -i;

}

}

// 前缀异或和 a[1]^...^a[i], 1

int pre(int i) {

int res = 0;

while (i > 0) {

res ^= tree[i];

i &= i - 1;

}

return res;

}

};

vector palindromePath(int n, vector>& edges, string s, vector& queries) {

// 建图

vector> g(n);

for (auto& e : edges) {

int x = e[0], y = e[1];

g[x].push_back(y);

g[y].push_back(x);

}

// 倍增层数

int mx = 0;

while ((1

if (mx == 0) mx = 1; // 至少保证 1 层

vector> pa(n, vector(mx, -1));

vector dep(n, 0);

vector timeIn(n), timeOut(n);

int clock = 0;

vector pathXorFromRoot(n, 0);

pathXorFromRoot[0] = 1

// DFS 递归函数

function dfs = [&](int x, int p) {

pa[x][0] = p;

++clock;

timeIn[x] = clock;

for (int y : g[x]) {

if (y != p) {

dep[y] = dep[x] + 1;

pathXorFromRoot[y] = pathXorFromRoot[x] ^ (1

dfs(y, x);

}

}

timeOut[x] = clock;

};

dfs(0, -1);

// 拓荒倍增表

for (int i = 0; i

for (int x = 0; x

int p = pa[x][i];

if (p != -1)

pa[x][i + 1] = pa[p][i];

else

pa[x][i + 1] = -1;

}

}

// 提高到指定深度

auto uptoDep = [&](int x, int d) {

int diff = dep[x] - d;

while (diff > 0) {

int lsb = diff & -diff;

int step = __builtin_ctz(lsb);

x = pa[x][step];

diff &= diff - 1; // diff -= lsb

}

return x;

};

// 最近大家祖宗

auto getLCA = [&](int x, int y) {

if (dep[x] > dep[y]) swap(x, y);

y = uptoDep(y, dep[x]);

if (y == x) return x;

for (int i = mx - 1; i >= 0; --i) {

int px = pa[x][i], py = pa[y][i];

if (px != py) {

x = px;

y = py;

}

}

return pa[x][0];

};

string t = s; // 可变字符数组

Fenwick bit(n); // 树状数组长度 n,下标 1..n

vector ans;

for (auto& q : queries) {

if (q[0] == 'u') { // update

int x; char c;

sscanf(q.c_str, "update %d %c", &x, &c);

char old = t[x];

int val = (1

t[x] = c;

// 子树区间差分

bit.update(timeIn[x], val);

bit.update(timeOut[x] + 1, val);

} else { // query

int x, y;

sscanf(q.c_str, "query %d %d", &x, &y);

int lca = getLCA(x, y);

int res = pathXorFromRoot[x] ^ pathXorFromRoot[y]

^ bit.pre(timeIn[x]) ^ bit.pre(timeIn[y])

^ (1

ans.push_back((res & (res - 1)) == 0);

}

}

return ans;

}

int main {

int n = 3;

vector> edges = {{0, 1}, {1, 2}};

string s = "aac";

vector queries = {"query 0 2", "update 1 b", "query 0 2"};

vector result = palindromePath(n, edges, s, queries);

// 输出驱逐

cout

for (size_t i = 0; i

if (i > 0) cout

cout

}

cout

return0;

}

咱们笃信东谈主工智能为世俗东谈主提供了一种“增强器具”,并勤勉于于共享全标的的AI学问。在这里,您不错找到最新的AI科普著作、器具评测、提高后果的隐私以及行业瞻念察。

宽饶情态“福大大架构师逐日一题”,发音书可赢得口试长途凤凰彩票_凤凰彩首页,让AI助力您的过去发展。



推荐资讯

凤凰彩票_凤凰彩首页 写给统共金牛座: 那些话, 你是不是也咽了一肚子?

凤凰彩首页 2026-05-13
你身边有莫得金牛座的东说念主,粗略你自个儿便是金牛。 跟他们相处深刻就会发现,金牛座大多是那种看着话未几、性子稳,以致有点慢热的东说念主。他们很少大吵大闹,很少逢东说念主就诉苦,更不会把我方的委曲和苦衷,歪邪摊开给别东说念主晒。 可惟有金牛...

凤凰彩票_凤凰彩首页 阿森纳本赛季弓手榜:约克雷斯21球、马丁内利11球、埃泽萨卡10球

凤凰彩首页 2026-05-30
世界杯滚球app中国官方下载 5月3日讯 阿森纳在本轮英超3-0完胜富勒姆,约克雷斯独造3球,也延续领跑队内弓手榜。 阿森纳本赛季各项赛事队内弓手榜: 21球,约克雷斯 11球,马丁内利 10球,埃泽、萨卡 7球,马杜埃凯、特罗萨德 6球,...

凤凰彩票_凤凰彩首页 求名求利, 但不利婚配之东谈主

凤凰彩首页 2026-05-13
一直看古道的著作,但愿能帮衬望望我家老二的。女孩,****年*月**日*时**分出身于江西宜春。 她当今玩心重,学习上不是很辛勤,收成一般,学其他敬爱爱好亦然闲散偷安,不知将来的学业处事如何?日坐伤官,是不是以后婚配会出问题? 古道评析: ...
    友情链接:

Copyright © 1998-2026 凤凰彩票官网首页 - Welcome™版权所有

cn-ylk.com备案号 备案号: 

技术支持:®凤凰彩票 RSS地图 HTML地图