当前位置: 首页 > news >正文

记录这辈子见到的第一道从上到下的树上倍增

这道题先是浪费我半个下午做,做不出来有时好久看题解实现,气死我了。

题意。

给定一张 \(N\) 点的树,让我们考虑断掉每一条边,统计分裂出的两个子树的重心编号和之和。

要求 \(O(nlogn)\) 或更优的时间复杂度。

做法

这个咋做呢?我们可以在 OIwiki 中发现一些关于树的重心的神秘性质。

我这里粘贴 OIwiki 原文了。

  • 一棵有根树的重心一定在根结点所在的重链上。一棵树的重心一定是该树根结点重子结点对应子树的重心的祖先。

这就非常的贴心了。

我们发现我们很好判断了,若想知道一个点是不是重心,我们仅仅需要判断它的重儿子和除它子树以外的那一部分就行了。

既然判断非常简单了,我们就可以倍增来找了。

有多个怎么办?重心有多个的话两个必然是相邻的,再加一个判断就行了。

为了方便我们就从上往下倍增了,这样我们往重儿子走处理起来会很方便。

至于断边的影响,我们可以单独处理考上断边的倍增数组,把直接的下一个改成次重儿子不就行了。

这个样子就好做多了,我们在 dfs 的时候就可以把每一条边搞定。

代码如下↓

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MN=4e6+315;
int T, n, totsize;
struct Node{int nxt, to;
}node[MN];
int head[MN], tottt;
void insert(int u, int v){node[++tottt].to=v;node[tottt].nxt=head[u];head[u]=tottt; return;
}
int jump[MN][21], fa[MN];
int siz[MN], tmpsiz[MN];
int secw[MN], ans=0;
void init(){for(int i=1; i<=n; ++i){head[i]=secw[i]=siz[i]=tmpsiz[i]=fa[i]=0;for(int j=0; j<=20; ++j) jump[i][j]=0;	}
}
void Pre(){for(int j=1; j<=20; ++j){for(int i=1; i<=n; ++i){if(jump[i][j-1]) jump[i][j]=jump[jump[i][j-1]][j-1];}}
}
void update(int u){for(int j=1; j<=20; ++j){if(jump[u][j-1])jump[u][j]=jump[jump[u][j-1]][j-1];else jump[u][j]=0;}
}
int query(int u, int rt){for(int i=20; i>=0; --i){if(jump[u][i]&&siz[rt]-siz[jump[u][i]]<=siz[rt]/2){u=jump[u][i];}}return u;
}
bool check(int u, int tot){return max(siz[jump[u][0]],tot-siz[u])<=tot/2;
}
void dfs1(int u, int father){tmpsiz[u]=1; fa[u]=father;for(int i=head[u];i;i=node[i].nxt){int v=node[i].to;if(v==father) continue;dfs1(v,u); tmpsiz[u]+=tmpsiz[v];if(tmpsiz[v]>tmpsiz[jump[u][0]]){secw[u]=jump[u][0];jump[u][0]=v;}else if(tmpsiz[v]>tmpsiz[secw[u]]){secw[u]=v;}}siz[u]=tmpsiz[u];
}
void dfs2(int u, int father){int now, sav=jump[u][0];for(int i=head[u];i;i=node[i].nxt){int v=node[i].to;if(v==father) continue;if(v==sav) jump[u][0]=secw[u];else jump[u][0]=sav;if(siz[father]>siz[jump[u][0]]) jump[u][0]=father;update(u); now=u;siz[u]=totsize-tmpsiz[v];siz[v]=tmpsiz[v];fa[u]=fa[v]=0;now=query(u,u);ans+=check(now,siz[u])*now+check(fa[now],siz[u])*fa[now];now=v; now=query(v,v);ans+=check(now,siz[v])*now+check(fa[now],siz[v])*fa[now];fa[u]=v; dfs2(v,u);}jump[u][0]=sav; siz[u]=tmpsiz[u];fa[u]=father; update(u);
}
signed main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin>>T; while(T--){init(); cin>>n;for(int i=1,u,v; i<n; ++i){cin>>u>>v; insert(u,v); insert(v,u);}ans=0; dfs1(1,0); Pre();totsize=siz[1]; dfs2(1,0);cout<<ans<<'\n';}return 0;
}
http://www.rkmt.cn/news/13042.html

相关文章:

  • 06.容器存储 - 教程
  • 深入解析:【Linux】进程概念(六):进程地址空间深度解析:虚拟地址与内存管理的奥秘
  • 深入解析:Metal - 5.深入剖析 3D 变换
  • 油猴脚本(tampermonkey)离线安装文件下载,带油猴(tampermonkey)插件清单
  • 详细介绍:【汽车篇】基于深度学习的2D+3D整车漆面外观缺陷检测
  • 深入解析:网线传输距离限制 | 理论基础 / 实际应用 | 双绞线分类与特性 / 水晶头制作
  • 2025年试验机品牌权威推荐榜:聚焦 TOP5 专精特新企业,疲劳试验机,压力试验机,液压万能试验机等设备技术实力与口碑解析!
  • [2025.9.27鲜花] 私たちもう一生 分かり合えないと 分かっていたでしょう
  • 2025年岗亭厂家最新权威推荐榜:内蒙古门卫室岗亭,售货岗亭,值班岗亭,保安岗亭,低噪声岗亭选购指南
  • SPI和普通设计模式区别
  • 混元开源之力:spring-ai-hunyuan 项目功能升级与实战体验 - 指南
  • 【题解】P13345 [EGOI 2025] IMO
  • 详细介绍:Python高效合并Excel多Sheet工作表,告别繁琐手动操作
  • Python爬虫的实现流程
  • 自动化运维工具 Ansible 集中化管理服务器 - 实践
  • 2025 北京羊蝎子餐厅推荐排行榜:TOP3最新必吃榜单,聚焦朝阳昌平东城等区域,揭秘北京羊蝎子餐厅必吃的门店!
  • Eurocrypt 2021 s Accepted Papers
  • Python 输入、输出的用法
  • 劝娃妈和娃不要学老人坐姿有感:老人无奈才坐成那样的。。AI协助分析很到位
  • 从“看得见”到“能决策”:Operation Intelligence 重构企业智能运维新范式 - 实践
  • 集训队互测投题——封印
  • Docker基础与工程部署
  • 安装MariaDB服务器流程介绍在Ubuntu 22.04系统
  • 三种神器让LLM输出结构化数据:LangChain、LlamaIndex与Function Calling实战指南
  • win11安装ensp
  • 无刷电机驱动工程及PID算法FOC算法完成(超高质量超高质量
  • 35Bourbaki1-1@《数学原理》1-1@20250927
  • Crypto 2021 s Accepted papers
  • 详细介绍:【数据结构】哈希表(Hash Table)详解
  • 250927