Warning: session_start(): open(/tmp/sess_aa8c00b74893663fd739cd27129396be, O_RDWR) failed: No space left on device (28) in /data/wiki/inc/init.php on line 239

Warning: session_start(): Failed to read session data: files (path: ) in /data/wiki/inc/init.php on line 239

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/inc/auth.php on line 430
Writing /data/wiki/data/cache/e/e8f32021df8a977906103ad4cec7e685.captchaip failed

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/inc/actions.php on line 38

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/lib/tpl/dokuwiki/main.php on line 12
technique:centroid_decomposition [CVBB ACM Team]

用户工具

站点工具


technique:centroid_decomposition

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
technique:centroid_decomposition [2020/05/31 20:17]
admin fix
technique:centroid_decomposition [2020/06/11 21:45] (当前版本)
admin finish
行 1: 行 1:
-**格式**:大部分已修改,请与原版对比 
-  - 注意句号 
-  - ''​\left(\right)''​ 通常用于括号内部内容较多的情况 
- 
 ====== 静态点分治 ====== ====== 静态点分治 ======
  
行 17: 行 13:
 如果选取树的重心作为根结点,则每棵子树的结点个数不超过 $\frac n2$ ,可以保证递归深度不超过 $\log n$。 如果选取树的重心作为根结点,则每棵子树的结点个数不超过 $\frac n2$ ,可以保证递归深度不超过 $\log n$。
  
-在这个基础上如果能在 $O(n)$ 时间维护经过根结点路径相关信息,则算法总时间复杂度为 $O(n\log n)$。+在这个基础上如果能在 $O(n\log^{\alpha}n)$ 时间维护经过根结点路径相关信息,则算法总时间复杂度为 $O(n\log^{\alpha+1}n)$,即复杂度只增加一个 $\log$。
  
 ===== 代码实现 ===== ===== 代码实现 =====
  
-重心的寻找可使用 dfs,处理出所有结点的 $\text{sz}$ ,所有结点的最大子树 $\text{mson}(u)=\max\left(\max\left(\text{sz}\left(\text{son}\left(u\right)\right),​\text{tot_sz}-\text{sz}\left(u\right)\right)\right)$。+重心的寻找可使用 ​''​dfs''​,处理出所有结点的 $\text{sz}$ ,所有结点的最大子树 $\text{mson}(u)=\max\left(\max\left(\text{sz}\left(\text{son}\left(u\right)\right),​\text{tot_sz}-\text{sz}\left(u\right)\right)\right)$。
  
 不断更新 $\text{mson}$ 最小的结点,最后便可以得到重心,时间复杂度 $O(n)$。 不断更新 $\text{mson}$ 最小的结点,最后便可以得到重心,时间复杂度 $O(n)$。
行 50: 行 46:
 } }
 void solve(int u){ void solve(int u){
 + int cur_sz=tot_sz;​
  vis[u]=true;​query(u);​  vis[u]=true;​query(u);​
  for(int i=head[u];​i;​i=edge[i].next){  for(int i=head[u];​i;​i=edge[i].next){
行 55: 行 52:
  if(vis[v])  if(vis[v])
  continue;  continue;
- tot_sz=sz[v];​root_sz=inf;​+ tot_sz=sz[v]>​sz[u]?​cur_sz-sz[u]:​sz[v];​root_sz=inf;​
  find_root(v,​u);​  find_root(v,​u);​
  solve(root);​  solve(root);​
行 143: 行 140:
 void query(int u){ void query(int u){
  sd.clear();​  sd.clear();​
 + mark[0]=true;​
  for(int i=head[u];​i;​i=edge[i].next){  for(int i=head[u];​i;​i=edge[i].next){
  d.clear();​  d.clear();​
行 166: 行 164:
 } }
 void solve(int u){ void solve(int u){
- vis[u]=mark[0]=true;​query(u);​+ int cur_sz=tot_sz;​ 
 + vis[u]=true;​query(u);​
  for(int i=head[u];​i;​i=edge[i].next){  for(int i=head[u];​i;​i=edge[i].next){
  int v=edge[i].to;​  int v=edge[i].to;​
  if(vis[v])  if(vis[v])
  continue;  continue;
- tot_sz=sz[v];​root_sz=inf;​+ tot_sz=sz[v]>​sz[u]?​cur_sz-sz[u]:​sz[v];​root_sz=inf;​
  find_root(v,​u);​  find_root(v,​u);​
  solve(root);​  solve(root);​
行 289: 行 288:
 } }
 void solve(int u){ void solve(int u){
 + int cur_sz=tot_sz;​
  vis[u]=true;​query(u);​  vis[u]=true;​query(u);​
  for(int i=head[u];​i;​i=edge[i].next){  for(int i=head[u];​i;​i=edge[i].next){
行 294: 行 294:
  if(vis[v])  if(vis[v])
  continue;  continue;
- tot_sz=sz[v];​root_sz=MAXN;​+ tot_sz=sz[v]>​sz[u]?​cur_sz-sz[u]:​sz[v];​root_sz=MAXN;​
  find_root(v,​u);​  find_root(v,​u);​
  solve(root);​  solve(root);​
行 334: 行 334:
 [[https://​www.luogu.com.cn/​problem/​UVA12161|UVA12161]] [[https://​www.luogu.com.cn/​problem/​UVA12161|UVA12161]]
  
-给定一棵 $n$ 个结点的树,每条边包含长度 $L$ 和费用 $D (1\le D,L \le 1000)$。选择一条总费用不超过 $m$ 的路径,使得路径总长度最大。+给定一棵 $n$ 个结点的树,每条边包含长度 $L$ 和费用 $D (1\le D,L \le 1000)$ 。选择一条总费用不超过 $m$ 的路径,使得路径总长度最大。
  
-考虑单调队列,时间复杂度 $O(n\log^2 n)$ 。另外本题存在 $O(n\log n)$ 解法,有兴趣的可以自己尝试。+考虑过根结点的路径,枚举到结点 $i$ 时,设它到根结点的路径费用为 $c(i)$。 
 + 
 +需要在已经枚举的其他子树中的结点中选取费用不超过 $D-c(i)$ 的最长路径。 
 + 
 +对结点 $v_1$ 和 $v_2$ ,如果 $v_1$ 费用大于 $v_2$ ,长度小于 $v_2$ ,那 $v_1$ 显然不会对后续答案产生贡献。 
 + 
 +因此可以考虑单调队列维护对答案有贡献的点集时间复杂度 $O(n\log^2 n)$。 
 + 
 +另外本题存在 $O(n\log n)$ 解法,有兴趣的可以自己尝试。
  
 ==== 习题5 ==== ==== 习题5 ====
行 344: 行 352:
 给定一棵 $n$ 个结点的树,树的每个节点有个颜色。 给定一棵 $n$ 个结点的树,树的每个节点有个颜色。
  
-定义 $s(i,j)$ 为 $i$ 到 $j$ 的颜色数量,$sum_i=\sum_{j=1}^n s(i,​j)$,要求输出所有 $sum_i$。+定义 $s(i,j)$ 为 $i$ 到 $j$ 的颜色数量,$\text{sum}_i=\sum_{j=1}^n s(i,​j)$,要求输出所有 $\text{sum}_i$。 
 + 
 +这题需要计算贡献,先考虑根结点,仅一端为根结点的路径对根结点的答案有贡献。 
 + 
 +考虑 dfs 处理子树,若一条路径上的某种颜色第一次出现,立刻计算它的贡献,贡献为它所在的子树大小。 
 + 
 +经过这遍 dfs ,可以得到所有子树到根结点的贡献。 
 + 
 +再考虑所有经过根结点的路径(包含一端为根结点的路径)对所有子树结点答案的影响。 
 + 
 +对每棵子树上的结点而言,所有经过根结点的路径可以分为一条从其他子树到根结点的路径(也可以是空路径)和一条从根结点到该结点的路径。 
 + 
 +可以考虑多次利用之前计算出的所有子树到根结点的贡献。 
 + 
 +对每棵子树,先消去该子树对根结点的贡献,便可以得到所有其他子树到根结点的路径贡献。 
 + 
 +再重新对该子树进行遍历,更新该子树上每个点的贡献值。 
 + 
 +最后把消去子树对根结点的贡献再改回去即可
  
-这题需要计算贡献,思路较复杂,这里只给出代码供参考,时间复杂度 $O(n\log n)$,另外本题存在 $O(n)$ 解法,有兴趣的可以自己尝试。+时间复杂度 $O(n\log n)$ ,另外本题存在 $O(n)$ 解法,有兴趣的可以自己尝试。
  
 <hidden 代码> <hidden 代码>
行 473: 行 499:
 } }
 void solve(int u){ void solve(int u){
 + int cur_sz=tot_sz;​
  vis[u]=true;​query(u);​  vis[u]=true;​query(u);​
  for(int i=head[u];​i;​i=edge[i].next){  for(int i=head[u];​i;​i=edge[i].next){
行 478: 行 505:
  if(vis[v])  if(vis[v])
  continue;  continue;
- tot_sz=sz[v];​root_sz=MAXN;​+ tot_sz=sz[v]>​sz[u]?​cur_sz-sz[u]:​sz[v];​root_sz=MAXN;​
  find_root(v,​u);​  find_root(v,​u);​
  solve(root);​  solve(root);​
technique/centroid_decomposition.1590927464.txt.gz · 最后更改: 2020/05/31 20:17 由 admin