树形dp

很明显我们可以枚举一条边,然后求两边的重心,这样是暴力,我们用一些奇怪的方法来优化这个找重心的过程,我们先预处理出来每个点最大和第二的儿子,然后每次把断掉的子树的贡献减掉,每次找重心就是向最大或第二大的儿子走,如果最大的儿子被减掉后比第二大的儿子小或者这条边被剪掉了,那么就向第二大的儿子走,这样复杂度是h的

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e4 + ;
int n;
ll ans = 1e18;
vector<int> G[N];
int dep[N], a[N], fa[N], son[N], bro[N];
ll sum[N], size[N];
void dfs(int u, int last)
{
size[u] = a[u];
fa[u] = last;
for(int i = ; i < G[u].size(); ++i)
{
int v = G[u][i];
if(v == last) continue;
dep[v] = dep[u] + ;
dfs(v, u);
size[u] += size[v];
sum[u] += sum[v] + size[v];
if(!son[u] || size[v] > size[son[u]]) bro[u] = son[u], son[u] = v;
else if(!bro[u] || size[v] > size[bro[u]]) bro[u] = v;
}
}
void center(ll &ret, int ban, ll tot, int u, ll S)
{
ret = min(ret, S);
int v = son[u];
if(v == ban || size[bro[u]] > size[v]) v = bro[u];
if(!v) return;
center(ret, ban, tot, v, S + tot - * size[v]);
}
void solve(int u, int last)
{
for(int i = ; i < G[u].size(); ++i)
{
int v = G[u][i];
if(v == last) continue;
for(int x = u; x; x = fa[x]) size[x] -= size[v];
ll tmp1 = 1e16, tmp2 = 1e16;
center(tmp1, v, size[], , sum[] - sum[v] - size[v] * dep[v]);
center(tmp2, , size[v], v, sum[v]);
ans = min(ans, tmp1 + tmp2);
for(int x = u; x; x = fa[x]) size[x] += size[v];
solve(v, u);
}
}
int main()
{
scanf("%d", &n);
for(int i = ; i < n; ++i)
{
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
for(int i = ; i <= n; ++i) scanf("%lld", &a[i]);
if(n <= )
{
puts("");
return ;
}
dfs(, );
solve(, );
printf("%lld\n", ans);
return ;
}

bzoj3302的更多相关文章

  1. BZOJ3302: [Shoi2005]树的双中心

    BZOJ3302: [Shoi2005]树的双中心 https://lydsy.com/JudgeOnline/problem.php?id=3302 分析: 朴素算法 : 枚举边,然后在两个连通块内 ...

  2. 【BZOJ3302】[Shoi2005]树的双中心 DFS

    [BZOJ3302][Shoi2005]树的双中心 Description Input 第一行为N,1<N<=50000,表示树的节点数目,树的节点从1到N编号.接下来N-1行,每行两个整 ...

  3. bzoj3302&bzoj2447&bzoj2103(树的重心)

    三倍的幸福! 暴力的做法就是枚举每一条边断开,选的两个点就是左右两棵树的重心. 可以发现找重心的时候一定是往权和大的子树找的,需要维护一个点的最大和次大子树,因为最大子树可能被割掉了,实际效率为O(N ...

随机推荐

  1. Linux配置防火墙添加端口(Ubuntu/Debian无法使用此方法)

    注意:Ubuntu/Debian无法使用此方法 1.打开iptables vi /etc/sysconfig/iptables 2.添加防火墙规则 规则参考:http://www.cnblogs.co ...

  2. android 播放MP3

    <?xml version="1.0" encoding="utf-8"?> <!-- 定义当前布局的基本LinearLayout --> ...

  3. 【Todo】Spark运行架构

    接上一篇:http://www.cnblogs.com/charlesblc/p/6108105.html 上一篇文章中主要参考的是 Link 这个系列下一篇讲的是Idea,没有细看,又看了再下一篇: ...

  4. 【c专家编程】分析c语言的声明

    联合: 在结构中,每个成员依次存储,而在联合中,所有成员都从偏移地址零开始存储,联合一般被用来节省空间,用法和struct相同. union bits32_tag { int whole; // 一个 ...

  5. 【转】 使用 Python 获取 Linux 系统信息

    在本文中,我们将会探索使用Python编程语言工具来检索Linux系统各种信息.走你. 哪个Python版本? 当我提及Python,所指的就是CPython 2(准确的是2.7).我会显式提醒那些相 ...

  6. 静态NAT、动态NAT、PAT(端口多路复用)的配置

    静态NAT.动态NAT.PAT(端口多路复用)的配置   NAT的实现方式有三种,即静态转换Static Nat.动态转换Dynamic Nat 和 端口多路复用OverLoad.    静态转换 ( ...

  7. 王立平--GUI与GUILayout的差别

    GUI.Button (new Rect(0,0,5,5,"哈哈"); GUILayout.Button ("heheh"); 1.以上代码都是现实一个butt ...

  8. redux 及 相关插件 项目实战

    目录结构 +-- app | +-- actions | +-- index.js | +-- components | +-- content.js | +-- footer.js | +-- se ...

  9. 双向队列(STL做法)

    双向队列 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描写叙述 想想双向链表--双向队列的定义差点儿相同,也就是说一个队列的队尾同一 ...

  10. C++类中使用new及delete小例子

    //默认复制构造函数的不足//尽管有默认的复制构造函数来解决一般对象与对象之间的初始化问题, 但是在有些情况下我们必须手动显式的去定义复制构造函数, 例如: #include <iostream ...