Description

\[\sum_{(u,v)\in E}\Biggl(\sum_{x为S_u重心}x+\sum_{y为S_v重心}y\Biggr)
\]

\(1\leqslant n\leqslant 300000\)

Solution

退役选手居然还能不看题解过题。

首先容易看出一定是对于每个点统计它可以作为几个重心,断掉的边一定在某个子树中,设重心为 \(u\) ,子树为 \(v\) ,\(u\) 其他出边 \(siz\) 最大为 \(maxsiz\) ,和为 \(sumsiz\) ,\(v\) 大小为 \(vsiz\) ,砍掉部分为 \(s\) ,则有 \(\max(maxsiz,vsiz-s)\leqslant\lfloor\frac{n-s}2\rfloor\) ,然后分类讨论一下其实是 \(vsiz\times 2-n\leqslant s\leqslant n-maxsiz\times 2\) ,于是不难想到用线段树维护 \(s\) 的集合,这时可以写出一个可持久化线段树合并 \(+\) 二次扫描与换根的做法,实际写的时候会发现二次扫描时根本没有合并,于是用一个树状数组进入回溯时修改即可。

Code

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int res = 0;
char c = getchar();
while(!isdigit(c))c = getchar();
while(isdigit(c))
{
res = (res << 1) + (res << 3) + c - '0';
c = getchar();
}
return res;
}
#define MAXN 300000
int n;
struct edge
{
int to,nxt;
}e[MAXN << 1];
int edgenum = 0,lin[MAXN] = {0};
void add(int a,int b)
{
e[++edgenum].to = b;e[edgenum].nxt = lin[a];lin[a] = edgenum;
e[++edgenum].to = a;e[edgenum].nxt = lin[b];lin[b] = edgenum;
return;
}
#define mid ((l + r) >> 1)
struct node
{
int lc,rc,sum;
}t[MAXN * 60];
int ptr = 0;
int newnode(){return ++ptr;}
int root[MAXN];
void insert(int &rt,int p,int val,int l,int r)
{
if(rt == 0)rt = newnode();
if(l == r){t[rt].sum += val;return;}
if(p <= mid)insert(t[rt].lc,p,val,l,mid);
else insert(t[rt].rc,p,val,mid + 1,r);
t[rt].sum = t[t[rt].lc].sum + t[t[rt].rc].sum;
return;
}
int merge(int a,int b,int l,int r)
{
if(a == 0 || b == 0)return a + b;
if(l == r){t[a].sum += t[b].sum;return a;}
t[a].lc = merge(t[a].lc,t[b].lc,l,mid);
t[a].rc = merge(t[a].rc,t[b].rc,mid + 1,r);
t[a].sum = t[t[a].lc].sum + t[t[a].rc].sum;
return a;
}
int query(int rt,int L,int R,int l,int r)
{
if(rt == 0)return 0;
if(L <= l && r <= R)return t[rt].sum;
int res = 0;
if(L <= mid)res += query(t[rt].lc,L,R,l,mid);
if(R > mid)res += query(t[rt].rc,L,R,mid + 1,r);
return res;
}
long long ans = 0;
int siz[MAXN];
int sum[MAXN];
int c[MAXN];
int lowbit(int x){return x & (-x);}
void qadd(int p,int x){for(int i = p;i <= n;i += lowbit(i))c[i] += x;return;}
int query(int p){int res = 0;for(int i = p;i >= 1;i -= lowbit(i))res += c[i];return res;}
int query(int l,int r)
{
l = max(l,1);r = min(r,n);
return query(r) - query(l - 1);
}
void init()
{
edgenum = 0;
memset(lin,0,sizeof(lin));
ans = 0;
memset(siz,0,sizeof(siz));
ptr = 0;
memset(t,0,sizeof(t));
memset(root,0,sizeof(root));
memset(sum,0,sizeof(sum));
memset(c,0,sizeof(c));
return;
}
int premax[MAXN],sufmax[MAXN];
vector<int> v;
int fa[MAXN];
void calc1(int k)
{
siz[k] = 1;
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(e[i].to == fa[k])continue;
fa[e[i].to] = k;
calc1(e[i].to);
siz[k] += siz[e[i].to];
}
insert(root[k],siz[k],1,1,n);
v.clear();v.push_back(0);
for(int i = lin[k];i != 0;i = e[i].nxt)if(e[i].to != fa[k])v.push_back(e[i].to);
int cnt = v.size() - 1;
sufmax[cnt + 1] = 0;
for(int i = 1;i <= cnt;++i)premax[i] = max(premax[i - 1],siz[v[i]]);
for(int i = cnt;i >= 1;--i)sufmax[i] = max(sufmax[i + 1],siz[v[i]]);
for(int i = 1;i <= cnt;++i)
{
int maxsiz = max(max(premax[i - 1],sufmax[i + 1]),n - siz[k]);
sum[k] += query(root[v[i]],siz[v[i]] * 2 - n,n - maxsiz * 2,1,n);
}
for(int i = lin[k];i != 0;i = e[i].nxt)if(e[i].to != fa[k])root[k] = merge(root[k],root[e[i].to],1,n);
if(k != 1)
{
int maxsiz = 0;
for(int i = lin[k];i != 0;i = e[i].nxt)if(e[i].to != fa[k])maxsiz = max(maxsiz,siz[e[i].to]);
int L = (n - siz[k]) * 2 - n,R = n - maxsiz * 2;
sum[k] -= query(root[k],L,R,1,n);
}
return;
}
void calc2(int k)
{
if(k != 1)
{
qadd(siz[k],-1);
qadd(n - siz[k],1);
}
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(e[i].to == fa[k])continue;
calc2(e[i].to);
}
if(k != 1)
{
int maxsiz = 0;
for(int i = lin[k];i != 0;i = e[i].nxt)if(e[i].to != fa[k])maxsiz = max(maxsiz,siz[e[i].to]);
int L = (n - siz[k]) * 2 - n,R = n - maxsiz * 2;
qadd(siz[k],1);
qadd(n - siz[k],-1);
sum[k] += query(L,R);
if(maxsiz == 0)--sum[k];
if((n - siz[k]) * 2 - n <= n - siz[k] && n - siz[k] <= n - maxsiz * 2)++sum[k];
}
return;
}
void solve()
{
init();
n = read();
for(int i = 1;i < n;++i)add(read(),read());
calc1(1);
for(int i = 1;i <= n;++i)qadd(i,query(root[1],i,i,1,n));
calc2(1);
for(int k = 1;k <= n;++k)ans += 1ll * k * sum[k];
cout << ans << endl;
return;
}
int main()
{
int testcases = read();
while(testcases--)solve();
return 0;
}

NOIP2019 树的重心的更多相关文章

  1. POJ3107Godfather[树形DP 树的重心]

    Godfather Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 6121   Accepted: 2164 Descrip ...

  2. poj1655 树的重心 树形dp

    树的重心定义为:找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡. 处理处每个节点的孩子有几个,和树的大小就好了. #include< ...

  3. poj3107 求树的重心(&& poj1655 同样求树的重心)

    题目链接:http://poj.org/problem?id=3107 求树的重心,所谓树的重心就是:在无根树转换为有根树的过程中,去掉根节点之后,剩下的树的最大结点最小,该点即为重心. 剩下的数的 ...

  4. 树形DP求树的重心 --SGU 134

    令一个点的属性值为:去除这个点以及与这个点相连的所有边后得到的连通分量的节点数的最大值. 则树的重心定义为:一个点,这个点的属性值在所有点中是最小的. SGU 134 即要找出所有的重心,并且找出重心 ...

  5. 求树的重心(POJ1655)

    题意:给出一颗n(n<=2000)个结点的树,删除其中的一个结点,会形成一棵树,或者多棵树,定义删除任意一个结点的平衡度为最大的那棵树的结点个数,问删除哪个结点后,可以让平衡度最小,即求树的重心 ...

  6. codeforces 685B Kay and Snowflake 树的重心

    分析:就是找到以每个节点为根节点的树的重心 树的重心可以看这三篇文章: 1:http://wenku.baidu.com/link?url=yc-3QD55hbCaRYEGsF2fPpXYg-iO63 ...

  7. POJ 1655 Balancing Act (求树的重心)

    求树的重心,直接当模板吧.先看POJ题目就知道重心什么意思了... 重心:删除该节点后最大连通块的节点数目最小 #include<cstdio> #include<cstring&g ...

  8. POJ3107--Godfather(树的重心)

    vector建图被卡了..改为链式前向星500ms过的..差了四倍多?... 表示不太会用链表建图啊..自己试着写的,没看模板..嗯..果然错了..落了一句话orz 树的重心就是找到一个树中一个点,其 ...

  9. POJ 1655 Balancing Act&&POJ 3107 Godfather(树的重心)

    树的重心的定义是: 一个点的所有子树中节点数最大的子树节点数最小. 这句话可能说起来比较绕,但是其实想想他的字面意思也就是找到最平衡的那个点. POJ 1655 题目大意: 直接给你一棵树,让你求树的 ...

  10. poj1655 Balancing Act 找树的重心

    http://poj.org/problem? id=1655 Balancing Act Time Limit: 1000MS   Memory Limit: 65536K Total Submis ...

随机推荐

  1. JS回文检查(FreeCodeCamp项目)

    需求 如果传入的字符串是回文字符串,则返回 true. 否则返回 false 回文 palindrome,指在忽略标点符号.大小写和空格的前提下,正着读和反着读一模一样. 注意:检查回文时,你需要先去 ...

  2. Ubuntu16.04设置root以及root用户自动登录

    自己之前做的笔记确实不是很详细.因为像是一些环境的配置,即使是这样做,但是自己大概率下是不知道这样做的原因,所以很多东西能不能弄好是有很大的运气成分在里面.所以就很需要记录下自己到底干了些什么. 所以 ...

  3. wmi和wmic

    https://baike.baidu.com/item/Windows%20Management%20Instrumentation/572501?fromtitle=WMI&fromid= ...

  4. java技术系列(四) 泛型

    泛型声明 有限制泛型声明方式: //确保class即表示枚举有表示Operation的子类型: private static <T extends Enum<T> & Ope ...

  5. java的知识点

    java 知识点 1.包装类自带有parse方法 Integer i = 315; int i1 = Integer.parseInt("315"); System.out.pri ...

  6. Java-面向对象进阶 继承限制

    1.子类可以继承父类的那些资源 private成员 子类和父类不在同一个包,使用默认访问权限的成员 构造方法

  7. iOS底层原理01:源码探索的三种方式

    ios 开发探索源码三种方法 1.下符号断点的形式直接跟流程 2.通过摁住 control + step  into 3.汇编查看跟流程 1.符号断点直接跟流程 以alloc为例: 选择断点Symbo ...

  8. 2022-3-21内部群每日三题-清辉PMP

    1.Sprint的速度低于预期,敏捷团队成员应该怎么做? A.限制团队成员和客户之间的沟通,以防止不必要的焦虑. B.管理沟通,以重置相应团队成员和干系人的期望. C.管理几名团队成员和客户之间的沟通 ...

  9. mybatis plus 分页总查出来全部数据

    需要配置过滤器 package com.tyyy.example.coreurl.config; import com.baomidou.mybatisplus.annotation.DbType; ...

  10. usbip:(二)从linux内核了解usb

    一.前言 1.首先了解一下EHCI.UHCI和OHCI. 从硬件上来说,usb 设备要想工作,除了外设本身,必须依赖于 usb host controller.一般来说,一个电脑里有一个 usb ho ...