题目

考场上送$75pts$真实良心,正解不难;考虑直接对于每一个点算割掉多少条边能使得这个点成为重心,不难发现对于一个不是重心的点,我们要割掉的那条边一定在那个大于$\lfloor \frac{2} \rfloor$的子树里面,而最大子树割掉之后可能就不是最大的了,但新的最大子树只可能是原来的次大子树,推一下柿子要割掉的子树大小需要在$[2A-n,n-2B]$之间,其中$A$为最大子树,$B$为次大子树

于是我们先求一个重心作为根,这样所有非重心节点的最大子树就会跨过这个根,在dfs的过程就能更新子树的大小,用树状数组维护一下就好了,由于需要排除子树内部的情况,所以还需要一个线段树合并;至于重心节点不超过两个,暴力求一下就好

代码

#include<bits/stdc++.h>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int maxn=3e5+5;
const int M=maxn*25;
int l[M],r[M],d[M];
struct E{int v,nxt;}e[maxn<<1];
int T,n,num,__,cnt;LL ans=0;
int head[maxn],sum[maxn],rt[maxn],mx[maxn],c[maxn],t[maxn],sz[maxn],col[maxn];
inline void add(int x,int v) {
for(re int i=x;i<=n;i+=i&(-i)) c[i]+=v;
}
inline int ask(int x) {
int nw=0;
for(re int i=x;i;i-=i&(-i)) nw+=c[i];
return nw;
}
inline void add_E(int x,int y) {
e[++num].v=y;e[num].nxt=head[x];head[x]=num;
}
void Dfs(int x,int fa) {
sum[x]=1;mx[x]=0;
for(re int i=head[x];i;i=e[i].nxt) {
if(e[i].v==fa) continue;
Dfs(e[i].v,x);sum[x]+=sum[e[i].v];mx[x]=max(mx[x],sum[e[i].v]);
}
mx[x]=max(n-sum[x],mx[x]);
}
int ins(int nw,int x,int y,int pos) {
if(!nw) nw=++cnt,d[nw]=l[nw]=r[nw]=0;d[nw]++;
if(x==y) return nw;
int mid=x+y>>1;
if(pos<=mid) l[nw]=ins(l[nw],x,mid,pos);
else r[nw]=ins(r[nw],mid+1,y,pos);
return nw;
}
int merge(int a,int b,int x,int y) {
if(!a||!b) return a|b;
if(x==y) {
d[a]+=d[b];
return a;
}
int mid=x+y>>1;
l[a]=merge(l[a],l[b],x,mid);r[a]=merge(r[a],r[b],mid+1,y);
d[a]=d[l[a]]+d[r[a]];return a;
}
int query(int nw,int x,int y,int lx,int ry) {
if(!nw||lx>ry) return 0;
if(lx<=x&&ry>=y) return d[nw];
int mid=x+y>>1,h=0;
if(lx<=mid) h+=query(l[nw],x,mid,lx,ry);
if(ry>mid) h+=query(r[nw],mid+1,y,lx,ry);
return h;
}
void dfs(int x,int fa) {
rt[x]=ins(rt[x],1,n,sz[x]);
if(fa) add(sz[fa],-1),add(n-sz[x],1);
for(re int i=head[x];i;i=e[i].nxt)
if(e[i].v!=fa) dfs(e[i].v,x),rt[x]=merge(rt[x],rt[e[i].v],1,n);
if(mx[x]+mx[x]>n) {
int k=0;
if(mx[x]-t[x]>=2*mx[x]-n) k=ask(mx[x]-t[x])-ask(2*mx[x]-n-1);
if(mx[x]-t[x]<n-2*t[x]) k+=ask(n-2*t[x])-ask(mx[x]-t[x]);
k-=query(rt[x],1,n,2*mx[x]-n,mx[x]-t[x]);
k-=query(rt[x],1,n,mx[x]-t[x]+1,n-2*t[x]);
ans+=1ll*k*x;
}
if(fa) add(sz[fa],1),add(n-sz[x],-1);
}
void Dfs_(int x,int fa) {
sz[x]=1,t[x]=0;
for(re int i=head[x];i;i=e[i].nxt) {
if(e[i].v==fa) continue;
Dfs_(e[i].v,x);sz[x]+=sz[e[i].v];t[x]=max(t[x],sz[e[i].v]);
}
}
void DFs(int x,int fa,int cm) {
col[x]=cm;sz[x]=1;
for(re int i=head[x];i;i=e[i].nxt) {
if(e[i].v==fa) continue;
DFs(e[i].v,x,cm);sz[x]+=sz[e[i].v];
}
}
void solve(int Rt) {
int col_num=1,A=0,B=0;
for(re int i=head[Rt];i;i=e[i].nxt,col_num++) {
DFs(e[i].v,Rt,col_num);
if(sz[e[i].v]>=sz[A]) B=A,A=e[i].v;
else if(sz[e[i].v]>sz[B]) B=e[i].v;
}
for(re int i=1;i<=n;i++) {
if(i==Rt) continue;
if(col[i]!=col[A]&&2*sz[A]<=(n-sz[i])) ans+=Rt;
if(col[i]==col[A]&&2*max(sz[A]-sz[i],sz[B])<=(n-sz[i])) ans+=Rt;
}
}
int main() {
T=read();
for(re int Rt;T;--T) {
cnt=0;ans=0;n=read(),num=0,__=0;memset(head,0,sizeof(head));memset(rt,0,sizeof(rt));memset(c,0,sizeof(c));
for(re int x,y,i=1;i<n;i++) x=read(),y=read(),add_E(x,y),add_E(y,x);
Dfs(1,0);for(re int i=1;i<=n;i++) if(mx[i]+mx[i]<=n) Rt=i;
Dfs_(Rt,0);
for(re int i=1;i<=n;i++) add(sz[i],1);dfs(Rt,0);
for(re int i=1;i<=n;i++) if(mx[i]+mx[i]<=n) solve(i);
printf("%lld\n",ans);
}
return 0;
}

「CSP-S 2019」树的重心的更多相关文章

  1. 【CSP-S 2019】树的重心(重心的性质)

    Description 给定一颗 \(n\) 个顶点的树 \(\text T\),共 \(n-1\) 次断边操作,每次将树分为两部分 \(\text T_1, \text T_2\),求: \[\su ...

  2. 「LOJ 3153」 「JOI Open 2019」三级跳

    题面 LOJ 3153 solution 对于任意一对\(A,B\),若区间\([A,B]\)中存在一个数权值大于\(A\)或\(B\),则用这个数来替代\(A\)或\(B\)显然更优. 故只需要考虑 ...

  3. LOJ#6713. 「EC Final 2019」狄利克雷 k 次根 加强版

    题目描述 定义两个函数 \(f, g: \{1, 2, \dots, n\} \rightarrow \mathbb Z\) 的狄利克雷卷积 \(f * g\) 为: \[ (f * g)(n) = ...

  4. 「WC 2019」数树

    「WC 2019」数树 一道涨姿势的EGF好题,官方题解我并没有完全看懂,尝试用指数型生成函数和组合意义的角度推了一波.考场上只得了 44 分也暴露了我在数数的一些基本套路上的不足,后面的 \(\ex ...

  5. 【题解】#6622. 「THUPC 2019」找树 / findtree(Matrix Tree+FWT)

    [题解]#6622. 「THUPC 2019」找树 / findtree(Matrix Tree+FWT) 之前做这道题不理解,有一点走火入魔了,甚至想要一本近世代数来看,然后通过人类智慧思考后发现, ...

  6. 「雅礼集训 2017 Day7」跳蚤王国的宰相(树的重心)

    题面 来源 「 雅 礼 集 训 2017 D a y 7 」 跳 蚤 王 国 的 宰 相   传 统 2000   m s 1024   M i B {\tt「雅礼集训 2017 Day7」跳蚤王国的 ...

  7. BZOJ 3510 - 首都 「 $LCT$ 动态维护树的重心」

    这题 FlashHu 的优化思路值得借鉴 前置引理 树中所有点到某个点的距离和中,到重心的距离和是最小的. 把两棵树通过某一点相连得到一颗新的树,新的树的重心必然在连接原来两棵树重心的路径上. 一棵树 ...

  8. 「NOI2013」树的计数 解题报告

    「NOI2013」树的计数 这什么神题 考虑对bfs重新编号为1,2,3...n,然后重新搞一下dfs序 设dfs序为\(dfn_i\),dfs序第\(i\)位对应的节点为\(pos_i\) 一个暴力 ...

  9. 「SDOI2017」树点涂色 解题报告

    「SDOI2017」树点涂色 我sb的不行了 其实一开始有一个类似动态dp的想法 每个点维护到lct树上到最浅点的颜色段数,然后维护一个\(mx_{0,1}\)也就是是否用虚儿子的最大颜色 用个set ...

随机推荐

  1. upc组队赛7 Slimming Plan

    Slimming Plan 题目描述 Chokudai loves eating so much. However, his doctor Akensho told him that he was o ...

  2. CVE-2010-4258漏洞分析

    Nelson Elhage最近发现了一个内核设计上的漏洞, 通过利用这个漏洞可以将一些以前只能dos的漏洞变成可以权限提升的漏洞. 当fork一个进程在的时候, copy_process执行如下操作: ...

  3. JS浏览器事件循环机制

    文章来自我的 github 博客,包括技术输出和学习笔记,欢迎star. 先来明白些概念性内容. 进程.线程 进程是系统分配的独立资源,是 CPU 资源分配的基本单位,进程是由一个或者多个线程组成的. ...

  4. js浮点金额计算精度

    在js中进行以元为单位进行浮点数计算时,会产生精度问题,例如: console.log(0.1+0.2) 结果为:0.30000000000000004 大多数编程语言计算采用的是IEEE 754 标 ...

  5. 将Eclipse项目转换成AndroidStudio项目过程中遇到的问题以及解决方法

    将Eclipse项目转换成AndroidStudio项目也不是第一次了,昨天转的时候遇到几个问题: 首先将项目导入androidstudio,导完后报错: 问题一: Error:java.util.c ...

  6. Shell内置命令——declare

  7. Java连接ActiveMQ代码示例(Producer和Consumer)

    import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; ...

  8. java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?

    有两种实现方法,分别是继承Thread类与实现Runnable接口用synchronized关键字修饰同步方法反对使用stop(),是因为它不安全.它会解除由线程获取的所有锁定,而且如果对象处于一种不 ...

  9. 关于_getattr_方法的一些理解

    在学习rest framework的过程中,rest framework的request是经过重构的,但是如果调用重构对象request中的属性,如果属性不存在会调用原request对象中的属性,它使 ...

  10. 9、Python 连接 PostgreSQL数据库 -- psycopg2

    1.cmd  pip install psycopg2 -- 提示错误信息 2.pip show pip   -->查看当前pip版本 3.python -m pip install --upg ...