「CSP-S 2019」树的重心
考场上送$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」树的重心的更多相关文章
- 【CSP-S 2019】树的重心(重心的性质)
Description 给定一颗 \(n\) 个顶点的树 \(\text T\),共 \(n-1\) 次断边操作,每次将树分为两部分 \(\text T_1, \text T_2\),求: \[\su ...
- 「LOJ 3153」 「JOI Open 2019」三级跳
题面 LOJ 3153 solution 对于任意一对\(A,B\),若区间\([A,B]\)中存在一个数权值大于\(A\)或\(B\),则用这个数来替代\(A\)或\(B\)显然更优. 故只需要考虑 ...
- LOJ#6713. 「EC Final 2019」狄利克雷 k 次根 加强版
题目描述 定义两个函数 \(f, g: \{1, 2, \dots, n\} \rightarrow \mathbb Z\) 的狄利克雷卷积 \(f * g\) 为: \[ (f * g)(n) = ...
- 「WC 2019」数树
「WC 2019」数树 一道涨姿势的EGF好题,官方题解我并没有完全看懂,尝试用指数型生成函数和组合意义的角度推了一波.考场上只得了 44 分也暴露了我在数数的一些基本套路上的不足,后面的 \(\ex ...
- 【题解】#6622. 「THUPC 2019」找树 / findtree(Matrix Tree+FWT)
[题解]#6622. 「THUPC 2019」找树 / findtree(Matrix Tree+FWT) 之前做这道题不理解,有一点走火入魔了,甚至想要一本近世代数来看,然后通过人类智慧思考后发现, ...
- 「雅礼集训 2017 Day7」跳蚤王国的宰相(树的重心)
题面 来源 「 雅 礼 集 训 2017 D a y 7 」 跳 蚤 王 国 的 宰 相 传 统 2000 m s 1024 M i B {\tt「雅礼集训 2017 Day7」跳蚤王国的 ...
- BZOJ 3510 - 首都 「 $LCT$ 动态维护树的重心」
这题 FlashHu 的优化思路值得借鉴 前置引理 树中所有点到某个点的距离和中,到重心的距离和是最小的. 把两棵树通过某一点相连得到一颗新的树,新的树的重心必然在连接原来两棵树重心的路径上. 一棵树 ...
- 「NOI2013」树的计数 解题报告
「NOI2013」树的计数 这什么神题 考虑对bfs重新编号为1,2,3...n,然后重新搞一下dfs序 设dfs序为\(dfn_i\),dfs序第\(i\)位对应的节点为\(pos_i\) 一个暴力 ...
- 「SDOI2017」树点涂色 解题报告
「SDOI2017」树点涂色 我sb的不行了 其实一开始有一个类似动态dp的想法 每个点维护到lct树上到最浅点的颜色段数,然后维护一个\(mx_{0,1}\)也就是是否用虚儿子的最大颜色 用个set ...
随机推荐
- python作业/练习/实战:生成双色球小程序
作业要求: 每注投注号码由6个红色球号码和1个蓝色球号码组成.红色球号码从1--33中选择:蓝色球号码从1--16中选择 代码范例 import random all_red_ball = [str( ...
- camunda授权的一些简单操作
/** * 授权操作 */public class ZccAuthorizationService { AuthorizationService authorizationService; @Befo ...
- kafka的简介
1. kafka是一个分布式消息队列.具有高性能.持久化.多副本备份.横向扩展能力.生产者往队列里写消息,消费者从队列里取消息进行业务逻辑.一般在架构设计中起到解耦.削峰.异步处理的作用. 1.1 b ...
- 条件选择case
SELECT COUNT(*),count(CASE b.AUTHORITY WHEN 'addAsmAccessControlList' THEN '1' ELSE NULL END) as aut ...
- python3中浮点数相减问题(大部分时候只需要关注整数的二进制形式就行了)
首先直接上判断方法:一切判断直接将整数部分装化成二进制,如果位数相等则相减后的值是等于的,如果不相等,相减大的结果一定不等于你要比较的结果. 如66.6-60.6==6.0的情况,66.6的二进制:1 ...
- DDCTF 北京地铁
这周打了ddctf,被打成了dd 北京地铁题目给了一张北京地铁图,提示如下:Color Threshold 提示:AES ECB密钥为小写字母提示2:密钥不足位用\0补全提示3:不要光记得隐写不看图片 ...
- 案例- CSS 三角加强
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Android编程之Listener侦听的N种写法及实现原理
写下这个题目时突然想起鲁迅笔下的孔乙已,茴香豆的几种写法,颇有些咬文嚼字的味道.虽然从事手机编程多年,但一直使用的是C和C++编程,由于安卓早期只支持JAVA开发,所以对于时下如火如荼的安卓系统,我一 ...
- Stm32CubeMX5 配置 STM32的串口DMA接受方式 --- 基于 stm32f051k8u6
实现的功能: 使用MDA方式把串口接受的数据在发送给串口(当然也可以做其他解析控制使用) 1. 先初始化 时钟使用外部的晶振配置系统时钟为48Mhz 2. 串口参数配置 3. 使能中断 4. 配置串 ...
- javaSE Comparable接口中的compareTo()方法
我们都知道,要对自建对象按照一定规则进行排序的话,要求自建对象实现Comparable接口,并重写compareTo() 方法,但compareTo() 方法的释义却不是那么容易搞清楚,下面举例进行阐 ...