[SHOI2005]树的双中心
题目链接:Click here
Solution:
首先我们要知道,选择两个点\(A,B\),必定存在一条边,割掉这条边,两个集合分别归\(A,B\)管
再结合题目,我们就得到了一个暴力的\(n^2\)做法:枚举个每条边,分别对两棵树求带权重心,更新答案
但这显然是过不了这道题的,考虑对求带权重心的过程进行优化:
设\(d(x)\)为\(x\)所在集合内所有点到他的距离之和,\(sz(x)\)表示以\(x\)为根的子树的大小,我们可以得到:
\]
其中\(u=fa(v)\),则若一个点\(v\)比\(u\)更优,即\(d(v)<d(u)\),可以得到\(2\times sz(v)>sz(rt)\)
显而易见的是,对于每一个\(u\),符合条件的\(v\)最多只有一个,则算法得到了很大的优化
我们对每一个点预处理出一个重儿子和次重儿子,处理出次重儿子的原因是割边后\(sz\)会发生变化
然后在更新过程中只要考虑当前重儿子是否满足条件即可,最坏时间复杂度\(O(n \times dep)\)
Code:
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+1;
int n,ans,cnt,no,head[N],f[N],a[N];
int fa[N],sz[N],son[N],nson[N],dep[N];
struct Edge{int nxt,to;}edge[N<<1];
void ins(int x,int y){
edge[++cnt].nxt=head[x];
edge[cnt].to=y;head[x]=cnt;
}
void dfs(int x,int fat){
sz[x]=a[x];fa[x]=fat;
for(int i=head[x];i;i=edge[i].nxt){
int y=edge[i].to;
if(y==fa[x]) continue;
dep[y]=dep[x]+1;
dfs(y,x);sz[x]+=sz[y];
if(sz[y]>sz[nson[x]]){
nson[x]=y;
if(sz[son[x]]<sz[nson[x]])
swap(son[x],nson[x]);
}f[x]+=f[y]+sz[y];
}
}
int calc(int x,int val,int u){
int y=son[x];if(son[x]==no||sz[nson[x]]>sz[son[x]]) y=nson[x];
if(y&&(sz[y]<<1)>sz[u]) return calc(y,val+sz[u]-(sz[y]<<1),u);return val;
}
void cut(){
for(int i=1;i<=cnt;i+=2){
int x=edge[i].to,y=edge[i+1].to;if(fa[y]!=x) swap(x,y);
no=y;for(int u=x;u;u=fa[u]) sz[u]-=sz[y];
int u1=calc(1,f[1]-f[y]-sz[y]*dep[y],1);
int u2=calc(y,f[y],y);ans=min(ans,u1+u2);
for(int u=x;u;u=fa[u]) sz[u]+=sz[y];
}
}
int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int main(){
n=read();ans=2147483647;
for(int i=1;i<n;i++){
int x=read(),y=read();
ins(x,y),ins(y,x);
}
for(int i=1;i<=n;i++) a[i]=read();
dfs(1,0);cut();printf("%d\n",ans);
return 0;
}
[SHOI2005]树的双中心的更多相关文章
- BZOJ3302: [Shoi2005]树的双中心
BZOJ3302: [Shoi2005]树的双中心 https://lydsy.com/JudgeOnline/problem.php?id=3302 分析: 朴素算法 : 枚举边,然后在两个连通块内 ...
- 【BZOJ3302】[Shoi2005]树的双中心 DFS
[BZOJ3302][Shoi2005]树的双中心 Description Input 第一行为N,1<N<=50000,表示树的节点数目,树的节点从1到N编号.接下来N-1行,每行两个整 ...
- 题解-SHOI2005 树的双中心
SHOI2005 树的双中心 给树 \(T=(V,E)(|V|=n)\),树高为 \(h\),\(w_u(u\in V)\).求 \(x\in V,y\in V:\left(\sum_{u\in V} ...
- 【BZOJ】3302: [Shoi2005]树的双中心 && 2103: Fire 消防站 && 2447: 消防站
[题意]给定带点权树,要求选择两个点x,y,满足所有点到这两个点中较近者的距离*点权的和最小.n<=50000,h<=100. [算法]树的重心 [题解]代码参考自:cgh_Andy 观察 ...
- luogu P2726 [SHOI2005]树的双中心
传送门 强行安利->巨佬题解 如果只有一个点贡献答案,那么答案显然是这棵树的带权重心,这个是可以\(O(n)\)求的.一个\(O(n^2)\)暴力是枚举两个集合之间的分界边,然后对这两个集合分别 ...
- 【洛谷 P2726】 [SHOI2005]树的双中心(树的重心)
先考虑一个\(O(N^2)\)做法. 设选的两个点为\(x,y\),则一定可以将树分成两个集合\(A,B\),使得\(A\)集合所有点都去\(x\),\(B\)集合所有点都去\(y\),而这两个集合的 ...
- bzoj 3302&2447&2103 树的双中心 树形DP
题目: 题解: bzoj 3302 == 2447 == 2103 三倍经验 首先我们考虑枚举两个中心的位置,然后统计答案. 我们发现,一定有一部分点离第一个中心更近,另一部分点离第二个中心更近 如果 ...
- 从Trie树到双数组Trie树
Trie树 原理 又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种.它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,能在常数时间O(len)内实现插入和查 ...
- python Trie树和双数组TRIE树的实现. 拥有3个功能:插入,删除,给前缀智能找到所有能匹配的单词
#coding=utf- #字典嵌套牛逼,别人写的,这样每一层非常多的东西,搜索就快了,树高26.所以整体搜索一个不关多大的单词表 #还是O(). ''' Python 字典 setdefault() ...
随机推荐
- P4411&&BZOJ1978 [BJWC2010]取数游戏(动态规划dp)
P4411 一道dp f[i]表示一定选第i个数的条件下前i个数所能得到的最优值 last[i]表示质因数i在数列a中最后出现时的下标 状态转移方程为\(f[i]=max\{f[last[j]\:|\ ...
- MySQL出现 Access denied for user 'root'@'localhost' (using password:YES) 解决办法
1.先停止MySQL服务,然后在MySQL安装目录(我的是C:\Program Files (x86)\MySQL\MySQL Server 5.1)找到my.ini文件,在最后一行添加skip-gr ...
- 洛谷 P1169 棋盘制作 题解
题面 这道题可以分成两部分来处理: 第一部分: 设f[i][j]表示右下角以(i,j)结尾的最大正方形的边长. 显然f[i][j]=min(f[i][j-1],f[i-1][j-1],f[i-1][j ...
- $Prufer$序列
\(Prufer\)序列 \(Prufer\)序列与树的相互转换: 树->\(Prufer\)序列 找到一个编号最小的叶子结点,把这个点删掉并且把跟他连着的那个点的编号加入\(Prufer\)序 ...
- js汉字转换为拼音
片段 1 片段 2 gistfile1.txt /* --- description: Pinyin, to get chinese pinyin from chinese. license: MIT ...
- Codeforces 1209E2. Rotate Columns (hard version)
传送门 发现 $n$ 很小,考虑状压 $dp$,但是如果强行枚举列并枚举置换再转移复杂度太高了 考虑推推结论,发现我们只要保留列最大值最大的 $n$ 列即可,证明好像挺显然: 假设我们让列最大值比较小 ...
- IWorkspace pWorkspace = pWorkspaceFactory.OpenFromFile(Application.StartupPath + "\\temp", 0); 报:异常来自 HRESULT:0x80040228
原因:未添加License. 解决办法:在窗体中添加LicenseControl即可.如下图,License图标运行时不会显示.
- C++ 大数运算(加减乘除取模)
加法:(字符串模拟小学加法) string add(string s1, string s2) { int len1 = s1.length(), len2 = s2.length(); ; '); ...
- wex5 windowDialog组件
windowDialog组件可以实现页面跳转 xid:windowDialog src:$UI/lohas/detailedknowledge.w 父页:myknowledge.w Model.pro ...
- 剑指offer-2:斐波那契数列
二.斐波那契数列 题目描述 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0). n<=39 1.递归法 1). 分析 斐波那契数列的标准公式为 ...