Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
GreyZeng
algorithm
提交
1aa04d02
A
algorithm
项目概览
GreyZeng
/
algorithm
通知
10
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
A
algorithm
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
1aa04d02
编写于
12月 15, 2022
作者:
GreyZeng
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
move to pkg
上级
423cb588
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
430 addition
and
430 deletion
+430
-430
src/main/java/snippet/Code_0017_TreeChainPartition.java
src/main/java/snippet/Code_0017_TreeChainPartition.java
+0
-429
src/main/java/数据结构/树链刨分/Code_TreeChainPartition.java
src/main/java/数据结构/树链刨分/Code_TreeChainPartition.java
+429
-0
src/main/java/算法/递归/Code_0016_GetMaxRecursive.java
src/main/java/算法/递归/Code_0016_GetMaxRecursive.java
+1
-1
未找到文件。
src/main/java/snippet/Code_0017_TreeChainPartition.java
已删除
100644 → 0
浏览文件 @
423cb588
package
snippet
;
import
java.util.*
;
//TODO
// 树链剖分
//给定数组father,大小为N,表示一共有N个节点
// father[i] = j 表示点i的父亲是点j, father表示的树一定是一棵树而不是森林
// 给定数组values,大小为N,values[i]=v表示节点i的权值是v
// 实现如下4个方法,保证4个方法都很快!
// 1)让某个子树所有节点值加上v,入参:int head, int v
// 2)查询某个子树所有节点值的累加和,入参:int head
// 3)在树上从a到b的整条链上所有加上v,入参:int a, int b, int v
// 4)查询在树上从a到b的整条链上所有节点值的累加和,入参:int a, int b
// 见笔记
public
class
Code_0017_TreeChainPartition
{
public
static
class
TreeChain
{
// 时间戳 0 1 2 3 4
private
int
tim
;
// 节点个数是n,节点编号是1~n
private
int
n
;
// 谁是头
private
int
h
;
// 朴素树结构,每个节点有哪些后代
private
int
[][]
tree
;
// 权重数组 原始的0节点权重是6 -> val[1] = 6
private
int
[]
val
;
// father数组一个平移,因为标号要+1
private
int
[]
fa
;
// 深度数组!
private
int
[]
dep
;
// son[i] = 0 i这个节点,没有儿子
// son[i] != 0 j i这个节点,重儿子是j
private
int
[]
son
;
// siz[i] i这个节点为头的子树,有多少个节点
private
int
[]
siz
;
// top[i] = j i这个节点,所在的重链,头是j
private
int
[]
top
;
// dfn[i] = j i这个节点,在dfs序中是第j个
private
int
[]
dfn
;
// 如果原来的节点a,权重是10
// 如果a节点在dfs序中是第5个节点, tnw[5] = 10
private
int
[]
tnw
;
// 线段树,在tnw上,玩连续的区间查询或者更新
private
SegmentTree
seg
;
public
TreeChain
(
int
[]
father
,
int
[]
values
)
{
// 原始的树 tree,弄好了,可以从i这个点,找到下级的直接孩子
// 上面的一大堆结构,准备好了空间,values -> val
// 找到头部点
initTree
(
father
,
values
);
// fa;
// dep;
// son;
// siz;
dfs1
(
h
,
0
);
// top;
// dfn;
// tnw;
dfs2
(
h
,
h
);
seg
=
new
SegmentTree
(
tnw
);
seg
.
build
(
1
,
n
,
1
);
}
private
void
initTree
(
int
[]
father
,
int
[]
values
)
{
tim
=
0
;
n
=
father
.
length
+
1
;
tree
=
new
int
[
n
][];
val
=
new
int
[
n
];
fa
=
new
int
[
n
];
dep
=
new
int
[
n
];
son
=
new
int
[
n
];
siz
=
new
int
[
n
];
top
=
new
int
[
n
];
dfn
=
new
int
[
n
];
tnw
=
new
int
[
n
--];
int
[]
cnum
=
new
int
[
n
];
System
.
arraycopy
(
values
,
0
,
val
,
1
,
n
);
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
if
(
father
[
i
]
==
i
)
{
h
=
i
+
1
;
}
else
{
cnum
[
father
[
i
]]++;
}
}
tree
[
0
]
=
new
int
[
0
];
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
tree
[
i
+
1
]
=
new
int
[
cnum
[
i
]];
}
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
if
(
i
+
1
!=
h
)
{
tree
[
father
[
i
]
+
1
][--
cnum
[
father
[
i
]]]
=
i
+
1
;
}
}
}
// 收集重儿子数量
// u 当前节点
// f u的父节点
private
void
dfs1
(
int
u
,
int
f
)
{
fa
[
u
]
=
f
;
dep
[
u
]
=
dep
[
f
]
+
1
;
siz
[
u
]
=
1
;
int
maxSize
=
-
1
;
for
(
int
v
:
tree
[
u
])
{
// 遍历u节点,所有的直接孩子
dfs1
(
v
,
u
);
siz
[
u
]
+=
siz
[
v
];
if
(
siz
[
v
]
>
maxSize
)
{
maxSize
=
siz
[
v
];
son
[
u
]
=
v
;
}
}
}
// 给每个重链的头节点标号,根据重儿子大小
// u当前节点
// t是u所在重链的头部
private
void
dfs2
(
int
u
,
int
t
)
{
dfn
[
u
]
=
++
tim
;
top
[
u
]
=
t
;
tnw
[
tim
]
=
val
[
u
];
if
(
son
[
u
]
!=
0
)
{
// 如果u有儿子 siz[u] > 1
dfs2
(
son
[
u
],
t
);
for
(
int
v
:
tree
[
u
])
{
if
(
v
!=
son
[
u
])
{
dfs2
(
v
,
v
);
}
}
}
}
// head为头的子树上,所有节点值+value
// 因为节点经过平移,所以head(原始节点) -> head(平移节点)
public
void
addSubtree
(
int
head
,
int
value
)
{
// 原始点编号 -> 平移编号
head
++;
// 平移编号 -> dfs编号 dfn[head]
seg
.
add
(
dfn
[
head
],
dfn
[
head
]
+
siz
[
head
]
-
1
,
value
,
1
,
n
,
1
);
}
public
int
querySubtree
(
int
head
)
{
head
++;
return
seg
.
query
(
dfn
[
head
],
dfn
[
head
]
+
siz
[
head
]
-
1
,
1
,
n
,
1
);
}
public
void
addChain
(
int
a
,
int
b
,
int
v
)
{
a
++;
b
++;
while
(
top
[
a
]
!=
top
[
b
])
{
// a和b不在同一条重链上
// 谁的头部在更深,谁先跳,防止跳过
if
(
dep
[
top
[
a
]]
>
dep
[
top
[
b
]])
{
seg
.
add
(
dfn
[
top
[
a
]],
dfn
[
a
],
v
,
1
,
n
,
1
);
a
=
fa
[
top
[
a
]];
}
else
{
seg
.
add
(
dfn
[
top
[
b
]],
dfn
[
b
],
v
,
1
,
n
,
1
);
b
=
fa
[
top
[
b
]];
}
}
// 如果a和b在同一条重链上
if
(
dep
[
a
]
>
dep
[
b
])
{
// 谁是DFS里面的小编号,谁放前
seg
.
add
(
dfn
[
b
],
dfn
[
a
],
v
,
1
,
n
,
1
);
}
else
{
seg
.
add
(
dfn
[
a
],
dfn
[
b
],
v
,
1
,
n
,
1
);
}
}
public
int
queryChain
(
int
a
,
int
b
)
{
a
++;
b
++;
int
ans
=
0
;
while
(
top
[
a
]
!=
top
[
b
])
{
if
(
dep
[
top
[
a
]]
>
dep
[
top
[
b
]])
{
ans
+=
seg
.
query
(
dfn
[
top
[
a
]],
dfn
[
a
],
1
,
n
,
1
);
a
=
fa
[
top
[
a
]];
}
else
{
ans
+=
seg
.
query
(
dfn
[
top
[
b
]],
dfn
[
b
],
1
,
n
,
1
);
b
=
fa
[
top
[
b
]];
}
}
if
(
dep
[
a
]
>
dep
[
b
])
{
ans
+=
seg
.
query
(
dfn
[
b
],
dfn
[
a
],
1
,
n
,
1
);
}
else
{
ans
+=
seg
.
query
(
dfn
[
a
],
dfn
[
b
],
1
,
n
,
1
);
}
return
ans
;
}
}
public
static
class
SegmentTree
{
private
int
MAXN
;
private
int
[]
arr
;
private
int
[]
sum
;
private
int
[]
lazy
;
public
SegmentTree
(
int
[]
origin
)
{
MAXN
=
origin
.
length
;
arr
=
origin
;
sum
=
new
int
[
MAXN
<<
2
];
lazy
=
new
int
[
MAXN
<<
2
];
}
private
void
pushUp
(
int
rt
)
{
sum
[
rt
]
=
sum
[
rt
<<
1
]
+
sum
[
rt
<<
1
|
1
];
}
private
void
pushDown
(
int
rt
,
int
ln
,
int
rn
)
{
if
(
lazy
[
rt
]
!=
0
)
{
lazy
[
rt
<<
1
]
+=
lazy
[
rt
];
sum
[
rt
<<
1
]
+=
lazy
[
rt
]
*
ln
;
lazy
[
rt
<<
1
|
1
]
+=
lazy
[
rt
];
sum
[
rt
<<
1
|
1
]
+=
lazy
[
rt
]
*
rn
;
lazy
[
rt
]
=
0
;
}
}
public
void
build
(
int
l
,
int
r
,
int
rt
)
{
if
(
l
==
r
)
{
sum
[
rt
]
=
arr
[
l
];
return
;
}
int
mid
=
(
l
+
r
)
>>
1
;
build
(
l
,
mid
,
rt
<<
1
);
build
(
mid
+
1
,
r
,
rt
<<
1
|
1
);
pushUp
(
rt
);
}
public
void
add
(
int
L
,
int
R
,
int
C
,
int
l
,
int
r
,
int
rt
)
{
if
(
L
<=
l
&&
r
<=
R
)
{
sum
[
rt
]
+=
C
*
(
r
-
l
+
1
);
lazy
[
rt
]
+=
C
;
return
;
}
int
mid
=
(
l
+
r
)
>>
1
;
pushDown
(
rt
,
mid
-
l
+
1
,
r
-
mid
);
if
(
L
<=
mid
)
{
add
(
L
,
R
,
C
,
l
,
mid
,
rt
<<
1
);
}
if
(
R
>
mid
)
{
add
(
L
,
R
,
C
,
mid
+
1
,
r
,
rt
<<
1
|
1
);
}
pushUp
(
rt
);
}
public
int
query
(
int
L
,
int
R
,
int
l
,
int
r
,
int
rt
)
{
if
(
L
<=
l
&&
r
<=
R
)
{
return
sum
[
rt
];
}
int
mid
=
(
l
+
r
)
>>
1
;
pushDown
(
rt
,
mid
-
l
+
1
,
r
-
mid
);
int
ans
=
0
;
if
(
L
<=
mid
)
{
ans
+=
query
(
L
,
R
,
l
,
mid
,
rt
<<
1
);
}
if
(
R
>
mid
)
{
ans
+=
query
(
L
,
R
,
mid
+
1
,
r
,
rt
<<
1
|
1
);
}
return
ans
;
}
}
// 为了测试,这个结构是暴力但正确的方法
public
static
class
Right
{
private
int
n
;
private
int
[][]
tree
;
private
int
[]
fa
;
private
int
[]
val
;
private
HashMap
<
Integer
,
Integer
>
path
;
public
Right
(
int
[]
father
,
int
[]
value
)
{
n
=
father
.
length
;
tree
=
new
int
[
n
][];
fa
=
new
int
[
n
];
val
=
new
int
[
n
];
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
fa
[
i
]
=
father
[
i
];
val
[
i
]
=
value
[
i
];
}
int
[]
help
=
new
int
[
n
];
int
h
=
0
;
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
if
(
father
[
i
]
==
i
)
{
h
=
i
;
}
else
{
help
[
father
[
i
]]++;
}
}
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
tree
[
i
]
=
new
int
[
help
[
i
]];
}
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
if
(
i
!=
h
)
{
tree
[
father
[
i
]][--
help
[
father
[
i
]]]
=
i
;
}
}
path
=
new
HashMap
<>();
}
public
void
addSubtree
(
int
head
,
int
value
)
{
val
[
head
]
+=
value
;
for
(
int
next
:
tree
[
head
])
{
addSubtree
(
next
,
value
);
}
}
public
int
querySubtree
(
int
head
)
{
int
ans
=
val
[
head
];
for
(
int
next
:
tree
[
head
])
{
ans
+=
querySubtree
(
next
);
}
return
ans
;
}
public
void
addChain
(
int
a
,
int
b
,
int
v
)
{
path
.
clear
();
path
.
put
(
a
,
null
);
while
(
a
!=
fa
[
a
])
{
path
.
put
(
fa
[
a
],
a
);
a
=
fa
[
a
];
}
while
(!
path
.
containsKey
(
b
))
{
val
[
b
]
+=
v
;
b
=
fa
[
b
];
}
val
[
b
]
+=
v
;
while
(
path
.
get
(
b
)
!=
null
)
{
b
=
path
.
get
(
b
);
val
[
b
]
+=
v
;
}
}
public
int
queryChain
(
int
a
,
int
b
)
{
path
.
clear
();
path
.
put
(
a
,
null
);
while
(
a
!=
fa
[
a
])
{
path
.
put
(
fa
[
a
],
a
);
a
=
fa
[
a
];
}
int
ans
=
0
;
while
(!
path
.
containsKey
(
b
))
{
ans
+=
val
[
b
];
b
=
fa
[
b
];
}
ans
+=
val
[
b
];
while
(
path
.
get
(
b
)
!=
null
)
{
b
=
path
.
get
(
b
);
ans
+=
val
[
b
];
}
return
ans
;
}
}
// 为了测试
// 随机生成N个节点树,可能是多叉树,并且一定不是森林
// 输入参数N要大于0
public
static
int
[]
generateFatherArray
(
int
N
)
{
int
[]
order
=
new
int
[
N
];
for
(
int
i
=
0
;
i
<
N
;
i
++)
{
order
[
i
]
=
i
;
}
for
(
int
i
=
N
-
1
;
i
>=
0
;
i
--)
{
swap
(
order
,
i
,
(
int
)
(
Math
.
random
()
*
(
i
+
1
)));
}
int
[]
ans
=
new
int
[
N
];
ans
[
order
[
0
]]
=
order
[
0
];
for
(
int
i
=
1
;
i
<
N
;
i
++)
{
ans
[
order
[
i
]]
=
order
[(
int
)
(
Math
.
random
()
*
i
)];
}
return
ans
;
}
// 为了测试
public
static
void
swap
(
int
[]
arr
,
int
i
,
int
j
)
{
int
tmp
=
arr
[
i
];
arr
[
i
]
=
arr
[
j
];
arr
[
j
]
=
tmp
;
}
// 为了测试
public
static
int
[]
generateValueArray
(
int
N
,
int
V
)
{
int
[]
ans
=
new
int
[
N
];
for
(
int
i
=
0
;
i
<
N
;
i
++)
{
ans
[
i
]
=
(
int
)
(
Math
.
random
()
*
V
)
+
1
;
}
return
ans
;
}
// 对数器
public
static
void
main
(
String
[]
args
)
{
int
N
=
50000
;
int
V
=
100000
;
int
[]
father
=
generateFatherArray
(
N
);
int
[]
values
=
generateValueArray
(
N
,
V
);
TreeChain
tc
=
new
TreeChain
(
father
,
values
);
Right
right
=
new
Right
(
father
,
values
);
int
testTime
=
1000000
;
System
.
out
.
println
(
"测试开始"
);
for
(
int
i
=
0
;
i
<
testTime
;
i
++)
{
double
decision
=
Math
.
random
();
if
(
decision
<
0.25
)
{
int
head
=
(
int
)
(
Math
.
random
()
*
N
);
int
value
=
(
int
)
(
Math
.
random
()
*
V
);
tc
.
addSubtree
(
head
,
value
);
right
.
addSubtree
(
head
,
value
);
}
else
if
(
decision
<
0.5
)
{
int
head
=
(
int
)
(
Math
.
random
()
*
N
);
if
(
tc
.
querySubtree
(
head
)
!=
right
.
querySubtree
(
head
))
{
System
.
out
.
println
(
"出错了!"
);
}
}
else
if
(
decision
<
0.75
)
{
int
a
=
(
int
)
(
Math
.
random
()
*
N
);
int
b
=
(
int
)
(
Math
.
random
()
*
N
);
int
value
=
(
int
)
(
Math
.
random
()
*
V
);
tc
.
addChain
(
a
,
b
,
value
);
right
.
addChain
(
a
,
b
,
value
);
}
else
{
int
a
=
(
int
)
(
Math
.
random
()
*
N
);
int
b
=
(
int
)
(
Math
.
random
()
*
N
);
if
(
tc
.
queryChain
(
a
,
b
)
!=
right
.
queryChain
(
a
,
b
))
{
System
.
out
.
println
(
"出错了!"
);
}
}
}
System
.
out
.
println
(
"测试结束"
);
}
}
src/main/java/数据结构/树链刨分/Code_TreeChainPartition.java
0 → 100644
浏览文件 @
1aa04d02
package
数据结构
.
树链刨分
;
import
java.util.*
;
// TODO
// 树链剖分
// 给定数组father,大小为N,表示一共有N个节点
// father[i] = j 表示点i的父亲是点j, father表示的树一定是一棵树而不是森林
// 给定数组values,大小为N,values[i]=v表示节点i的权值是v
// 实现如下4个方法,保证4个方法都很快!
// 1)让某个子树所有节点值加上v,入参:int head, int v
// 2)查询某个子树所有节点值的累加和,入参:int head
// 3)在树上从a到b的整条链上所有加上v,入参:int a, int b, int v
// 4)查询在树上从a到b的整条链上所有节点值的累加和,入参:int a, int b
// 见笔记
public
class
Code_TreeChainPartition
{
public
static
class
TreeChain
{
// 时间戳 0 1 2 3 4
private
int
tim
;
// 节点个数是n,节点编号是1~n
private
int
n
;
// 谁是头
private
int
h
;
// 朴素树结构,每个节点有哪些后代
private
int
[][]
tree
;
// 权重数组 原始的0节点权重是6 -> val[1] = 6
private
int
[]
val
;
// father数组一个平移,因为标号要+1
private
int
[]
fa
;
// 深度数组!
private
int
[]
dep
;
// son[i] = 0 i这个节点,没有儿子
// son[i] != 0 j i这个节点,重儿子是j
private
int
[]
son
;
// siz[i] i这个节点为头的子树,有多少个节点
private
int
[]
siz
;
// top[i] = j i这个节点,所在的重链,头是j
private
int
[]
top
;
// dfn[i] = j i这个节点,在dfs序中是第j个
private
int
[]
dfn
;
// 如果原来的节点a,权重是10
// 如果a节点在dfs序中是第5个节点, tnw[5] = 10
private
int
[]
tnw
;
// 线段树,在tnw上,玩连续的区间查询或者更新
private
SegmentTree
seg
;
public
TreeChain
(
int
[]
father
,
int
[]
values
)
{
// 原始的树 tree,弄好了,可以从i这个点,找到下级的直接孩子
// 上面的一大堆结构,准备好了空间,values -> val
// 找到头部点
initTree
(
father
,
values
);
// fa;
// dep;
// son;
// siz;
dfs1
(
h
,
0
);
// top;
// dfn;
// tnw;
dfs2
(
h
,
h
);
seg
=
new
SegmentTree
(
tnw
);
seg
.
build
(
1
,
n
,
1
);
}
private
void
initTree
(
int
[]
father
,
int
[]
values
)
{
tim
=
0
;
n
=
father
.
length
+
1
;
tree
=
new
int
[
n
][];
val
=
new
int
[
n
];
fa
=
new
int
[
n
];
dep
=
new
int
[
n
];
son
=
new
int
[
n
];
siz
=
new
int
[
n
];
top
=
new
int
[
n
];
dfn
=
new
int
[
n
];
tnw
=
new
int
[
n
--];
int
[]
cnum
=
new
int
[
n
];
System
.
arraycopy
(
values
,
0
,
val
,
1
,
n
);
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
if
(
father
[
i
]
==
i
)
{
h
=
i
+
1
;
}
else
{
cnum
[
father
[
i
]]++;
}
}
tree
[
0
]
=
new
int
[
0
];
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
tree
[
i
+
1
]
=
new
int
[
cnum
[
i
]];
}
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
if
(
i
+
1
!=
h
)
{
tree
[
father
[
i
]
+
1
][--
cnum
[
father
[
i
]]]
=
i
+
1
;
}
}
}
// 收集重儿子数量
// u 当前节点
// f u的父节点
private
void
dfs1
(
int
u
,
int
f
)
{
fa
[
u
]
=
f
;
dep
[
u
]
=
dep
[
f
]
+
1
;
siz
[
u
]
=
1
;
int
maxSize
=
-
1
;
for
(
int
v
:
tree
[
u
])
{
// 遍历u节点,所有的直接孩子
dfs1
(
v
,
u
);
siz
[
u
]
+=
siz
[
v
];
if
(
siz
[
v
]
>
maxSize
)
{
maxSize
=
siz
[
v
];
son
[
u
]
=
v
;
}
}
}
// 给每个重链的头节点标号,根据重儿子大小
// u当前节点
// t是u所在重链的头部
private
void
dfs2
(
int
u
,
int
t
)
{
dfn
[
u
]
=
++
tim
;
top
[
u
]
=
t
;
tnw
[
tim
]
=
val
[
u
];
if
(
son
[
u
]
!=
0
)
{
// 如果u有儿子 siz[u] > 1
dfs2
(
son
[
u
],
t
);
for
(
int
v
:
tree
[
u
])
{
if
(
v
!=
son
[
u
])
{
dfs2
(
v
,
v
);
}
}
}
}
// head为头的子树上,所有节点值+value
// 因为节点经过平移,所以head(原始节点) -> head(平移节点)
public
void
addSubtree
(
int
head
,
int
value
)
{
// 原始点编号 -> 平移编号
head
++;
// 平移编号 -> dfs编号 dfn[head]
seg
.
add
(
dfn
[
head
],
dfn
[
head
]
+
siz
[
head
]
-
1
,
value
,
1
,
n
,
1
);
}
public
int
querySubtree
(
int
head
)
{
head
++;
return
seg
.
query
(
dfn
[
head
],
dfn
[
head
]
+
siz
[
head
]
-
1
,
1
,
n
,
1
);
}
public
void
addChain
(
int
a
,
int
b
,
int
v
)
{
a
++;
b
++;
while
(
top
[
a
]
!=
top
[
b
])
{
// a和b不在同一条重链上
// 谁的头部在更深,谁先跳,防止跳过
if
(
dep
[
top
[
a
]]
>
dep
[
top
[
b
]])
{
seg
.
add
(
dfn
[
top
[
a
]],
dfn
[
a
],
v
,
1
,
n
,
1
);
a
=
fa
[
top
[
a
]];
}
else
{
seg
.
add
(
dfn
[
top
[
b
]],
dfn
[
b
],
v
,
1
,
n
,
1
);
b
=
fa
[
top
[
b
]];
}
}
// 如果a和b在同一条重链上
if
(
dep
[
a
]
>
dep
[
b
])
{
// 谁是DFS里面的小编号,谁放前
seg
.
add
(
dfn
[
b
],
dfn
[
a
],
v
,
1
,
n
,
1
);
}
else
{
seg
.
add
(
dfn
[
a
],
dfn
[
b
],
v
,
1
,
n
,
1
);
}
}
public
int
queryChain
(
int
a
,
int
b
)
{
a
++;
b
++;
int
ans
=
0
;
while
(
top
[
a
]
!=
top
[
b
])
{
if
(
dep
[
top
[
a
]]
>
dep
[
top
[
b
]])
{
ans
+=
seg
.
query
(
dfn
[
top
[
a
]],
dfn
[
a
],
1
,
n
,
1
);
a
=
fa
[
top
[
a
]];
}
else
{
ans
+=
seg
.
query
(
dfn
[
top
[
b
]],
dfn
[
b
],
1
,
n
,
1
);
b
=
fa
[
top
[
b
]];
}
}
if
(
dep
[
a
]
>
dep
[
b
])
{
ans
+=
seg
.
query
(
dfn
[
b
],
dfn
[
a
],
1
,
n
,
1
);
}
else
{
ans
+=
seg
.
query
(
dfn
[
a
],
dfn
[
b
],
1
,
n
,
1
);
}
return
ans
;
}
}
public
static
class
SegmentTree
{
private
int
MAXN
;
private
int
[]
arr
;
private
int
[]
sum
;
private
int
[]
lazy
;
public
SegmentTree
(
int
[]
origin
)
{
MAXN
=
origin
.
length
;
arr
=
origin
;
sum
=
new
int
[
MAXN
<<
2
];
lazy
=
new
int
[
MAXN
<<
2
];
}
private
void
pushUp
(
int
rt
)
{
sum
[
rt
]
=
sum
[
rt
<<
1
]
+
sum
[
rt
<<
1
|
1
];
}
private
void
pushDown
(
int
rt
,
int
ln
,
int
rn
)
{
if
(
lazy
[
rt
]
!=
0
)
{
lazy
[
rt
<<
1
]
+=
lazy
[
rt
];
sum
[
rt
<<
1
]
+=
lazy
[
rt
]
*
ln
;
lazy
[
rt
<<
1
|
1
]
+=
lazy
[
rt
];
sum
[
rt
<<
1
|
1
]
+=
lazy
[
rt
]
*
rn
;
lazy
[
rt
]
=
0
;
}
}
public
void
build
(
int
l
,
int
r
,
int
rt
)
{
if
(
l
==
r
)
{
sum
[
rt
]
=
arr
[
l
];
return
;
}
int
mid
=
(
l
+
r
)
>>
1
;
build
(
l
,
mid
,
rt
<<
1
);
build
(
mid
+
1
,
r
,
rt
<<
1
|
1
);
pushUp
(
rt
);
}
public
void
add
(
int
L
,
int
R
,
int
C
,
int
l
,
int
r
,
int
rt
)
{
if
(
L
<=
l
&&
r
<=
R
)
{
sum
[
rt
]
+=
C
*
(
r
-
l
+
1
);
lazy
[
rt
]
+=
C
;
return
;
}
int
mid
=
(
l
+
r
)
>>
1
;
pushDown
(
rt
,
mid
-
l
+
1
,
r
-
mid
);
if
(
L
<=
mid
)
{
add
(
L
,
R
,
C
,
l
,
mid
,
rt
<<
1
);
}
if
(
R
>
mid
)
{
add
(
L
,
R
,
C
,
mid
+
1
,
r
,
rt
<<
1
|
1
);
}
pushUp
(
rt
);
}
public
int
query
(
int
L
,
int
R
,
int
l
,
int
r
,
int
rt
)
{
if
(
L
<=
l
&&
r
<=
R
)
{
return
sum
[
rt
];
}
int
mid
=
(
l
+
r
)
>>
1
;
pushDown
(
rt
,
mid
-
l
+
1
,
r
-
mid
);
int
ans
=
0
;
if
(
L
<=
mid
)
{
ans
+=
query
(
L
,
R
,
l
,
mid
,
rt
<<
1
);
}
if
(
R
>
mid
)
{
ans
+=
query
(
L
,
R
,
mid
+
1
,
r
,
rt
<<
1
|
1
);
}
return
ans
;
}
}
// 为了测试,这个结构是暴力但正确的方法
public
static
class
Right
{
private
int
n
;
private
int
[][]
tree
;
private
int
[]
fa
;
private
int
[]
val
;
private
HashMap
<
Integer
,
Integer
>
path
;
public
Right
(
int
[]
father
,
int
[]
value
)
{
n
=
father
.
length
;
tree
=
new
int
[
n
][];
fa
=
new
int
[
n
];
val
=
new
int
[
n
];
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
fa
[
i
]
=
father
[
i
];
val
[
i
]
=
value
[
i
];
}
int
[]
help
=
new
int
[
n
];
int
h
=
0
;
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
if
(
father
[
i
]
==
i
)
{
h
=
i
;
}
else
{
help
[
father
[
i
]]++;
}
}
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
tree
[
i
]
=
new
int
[
help
[
i
]];
}
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
if
(
i
!=
h
)
{
tree
[
father
[
i
]][--
help
[
father
[
i
]]]
=
i
;
}
}
path
=
new
HashMap
<>();
}
public
void
addSubtree
(
int
head
,
int
value
)
{
val
[
head
]
+=
value
;
for
(
int
next
:
tree
[
head
])
{
addSubtree
(
next
,
value
);
}
}
public
int
querySubtree
(
int
head
)
{
int
ans
=
val
[
head
];
for
(
int
next
:
tree
[
head
])
{
ans
+=
querySubtree
(
next
);
}
return
ans
;
}
public
void
addChain
(
int
a
,
int
b
,
int
v
)
{
path
.
clear
();
path
.
put
(
a
,
null
);
while
(
a
!=
fa
[
a
])
{
path
.
put
(
fa
[
a
],
a
);
a
=
fa
[
a
];
}
while
(!
path
.
containsKey
(
b
))
{
val
[
b
]
+=
v
;
b
=
fa
[
b
];
}
val
[
b
]
+=
v
;
while
(
path
.
get
(
b
)
!=
null
)
{
b
=
path
.
get
(
b
);
val
[
b
]
+=
v
;
}
}
public
int
queryChain
(
int
a
,
int
b
)
{
path
.
clear
();
path
.
put
(
a
,
null
);
while
(
a
!=
fa
[
a
])
{
path
.
put
(
fa
[
a
],
a
);
a
=
fa
[
a
];
}
int
ans
=
0
;
while
(!
path
.
containsKey
(
b
))
{
ans
+=
val
[
b
];
b
=
fa
[
b
];
}
ans
+=
val
[
b
];
while
(
path
.
get
(
b
)
!=
null
)
{
b
=
path
.
get
(
b
);
ans
+=
val
[
b
];
}
return
ans
;
}
}
// 为了测试
// 随机生成N个节点树,可能是多叉树,并且一定不是森林
// 输入参数N要大于0
public
static
int
[]
generateFatherArray
(
int
N
)
{
int
[]
order
=
new
int
[
N
];
for
(
int
i
=
0
;
i
<
N
;
i
++)
{
order
[
i
]
=
i
;
}
for
(
int
i
=
N
-
1
;
i
>=
0
;
i
--)
{
swap
(
order
,
i
,
(
int
)
(
Math
.
random
()
*
(
i
+
1
)));
}
int
[]
ans
=
new
int
[
N
];
ans
[
order
[
0
]]
=
order
[
0
];
for
(
int
i
=
1
;
i
<
N
;
i
++)
{
ans
[
order
[
i
]]
=
order
[(
int
)
(
Math
.
random
()
*
i
)];
}
return
ans
;
}
// 为了测试
public
static
void
swap
(
int
[]
arr
,
int
i
,
int
j
)
{
int
tmp
=
arr
[
i
];
arr
[
i
]
=
arr
[
j
];
arr
[
j
]
=
tmp
;
}
// 为了测试
public
static
int
[]
generateValueArray
(
int
N
,
int
V
)
{
int
[]
ans
=
new
int
[
N
];
for
(
int
i
=
0
;
i
<
N
;
i
++)
{
ans
[
i
]
=
(
int
)
(
Math
.
random
()
*
V
)
+
1
;
}
return
ans
;
}
// 对数器
public
static
void
main
(
String
[]
args
)
{
int
N
=
50000
;
int
V
=
100000
;
int
[]
father
=
generateFatherArray
(
N
);
int
[]
values
=
generateValueArray
(
N
,
V
);
TreeChain
tc
=
new
TreeChain
(
father
,
values
);
Right
right
=
new
Right
(
father
,
values
);
int
testTime
=
1000000
;
System
.
out
.
println
(
"测试开始"
);
for
(
int
i
=
0
;
i
<
testTime
;
i
++)
{
double
decision
=
Math
.
random
();
if
(
decision
<
0.25
)
{
int
head
=
(
int
)
(
Math
.
random
()
*
N
);
int
value
=
(
int
)
(
Math
.
random
()
*
V
);
tc
.
addSubtree
(
head
,
value
);
right
.
addSubtree
(
head
,
value
);
}
else
if
(
decision
<
0.5
)
{
int
head
=
(
int
)
(
Math
.
random
()
*
N
);
if
(
tc
.
querySubtree
(
head
)
!=
right
.
querySubtree
(
head
))
{
System
.
out
.
println
(
"出错了!"
);
}
}
else
if
(
decision
<
0.75
)
{
int
a
=
(
int
)
(
Math
.
random
()
*
N
);
int
b
=
(
int
)
(
Math
.
random
()
*
N
);
int
value
=
(
int
)
(
Math
.
random
()
*
V
);
tc
.
addChain
(
a
,
b
,
value
);
right
.
addChain
(
a
,
b
,
value
);
}
else
{
int
a
=
(
int
)
(
Math
.
random
()
*
N
);
int
b
=
(
int
)
(
Math
.
random
()
*
N
);
if
(
tc
.
queryChain
(
a
,
b
)
!=
right
.
queryChain
(
a
,
b
))
{
System
.
out
.
println
(
"出错了!"
);
}
}
}
System
.
out
.
println
(
"测试结束"
);
}
}
src/main/java/
snippet
/Code_0016_GetMaxRecursive.java
→
src/main/java/
算法/递归
/Code_0016_GetMaxRecursive.java
浏览文件 @
1aa04d02
package
snippet
;
package
算法
.
递归
;
// 递归求一个数组中的最大值
public
class
Code_0016_GetMaxRecursive
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录