题目

考场上送$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. vue轮播,展示pdf

    vue轮播,展示pdf 根据左侧图片格式,右侧展示相应的pdf文件与图片.(vue中不支持pdf格式,pdf文件要放在static文件里):代码如下: <template> <!-- ...

  2. ACM_ICPC_Team

    题目: There are a number of people who will be attending ACM-ICPC World Finals. Each of them may be we ...

  3. 备份一下我的.bash_aliases文件

    # 这是陈悦老师的课程练习目录 alias cdchen="cd /home/branches/Documents/chen" # 每次grep都显示出行号 alias grep= ...

  4. 我的.eslintrc.js

    module.exports = { root: true, env: { node: true }, extends: ["plugin:vue/essential", &quo ...

  5. 面试题: nodejs 的事件轮询机制

    setTimeout(function(){ console.log('setTimeout()执行了') },0) setImmediate(function(){ console.log('set ...

  6. hbuilder模拟器端口

    模拟器 | 端口 夜神安卓模拟器夜神安卓模拟器     62001 逍遥安卓模拟器逍遥安卓模拟器     21503 BlueStacks(蓝叠安卓模拟器)BlueStacks(蓝叠安卓模拟器)    ...

  7. JNI 在Android Studio利用NDK编译运行一个简单的c库

    NDK开发,其实是为了项目需要调用底层的一些C/C++的一些东西:另外就是为了效率更加高些.如果你在Eclipse+ADT下开发过NDK就能体会到要么是配置NDK还要下载Cygwin,配置Cygwin ...

  8. docker build 时 alpine 无法安装软件问题的解决

    使用 alpine 作为 docker 基础镜像时,运行 apk add ..... 遇到如下错误: WARNING: Ignoring http://dl-cdn.alpinelinux.org/a ...

  9. 转。Nas配置。想找原版没找到,全是转载的,也没注出处,无语。

    随着家用宽带的不断提速和高清电影的普及外带单反的家庭占有率越来越搞,仅靠台式机里那几块硬盘越来越不够用了. 简单的计算了一下,家里的台式机上2T的容量(1T+640G+320G)已经接近于80%满,外 ...

  10. java中String类的面试题大全含答案

    1.下面程序的运行结果是()(选择一项)String str1="hello";String str2=new String("hello");System.o ...