\(\mathcal{Description}\)

  Link.

  给定一棵含 \(n\) 个结点的树。称点集 \(S\) 到结点 \(u\) 的会合距离为 \(\sum_{v\in S}\operatorname{dist}(u,v)\)。对于 \(|S|=1,2,\dots,n\),求使得满足 \(S\) 一定且 \(S\) 到 \(u\) 的会合距离最小时,可能选取的 \(u\) 的个数的最大值。

  \(n\le2\times10^5\)。

\(\mathcal{Solution}\)

  可以发现,\(u\) 为 \(S\) 在树上的带权重心。不难得到以下结论:

  • 当 \(2\not\mid|S|\),答案为 \(1\);
  • 若存在多个合法的 \(u\),这些 \(u\) 构成一条树链,且树链上任意一条边满足:断开这条边,两个联通块中属于 \(S\) 的结点个数相等(这也印证了上个结论)。

  考虑“枚举”树链更新答案。设一条树链两端结点构成的联通块大小为 \(s_x,s_y\),则满足 \(|S|\le2\min\{s_x,s_y\}\) 的 \(|S|\) 的答案都能用这条链的长度更新。注意到需要维护全局联通块大小这一信息,尝试钦定全局根后在有向树上做点分治。每层分治更新答案时,可以钦定当前得到的联通块的小为某条树链较小的一端,正反分别做一遍,用树状数组就能维护。

  复杂度 \(\mathcal O(n\log^2n)\)。

  UPD:注意到点分治取重心的性质,跨过上层分治中心的路径一定不优,所以也可以直接统计以分治中心为根的子树大小。写的时候需要注意点分的实现,分治中心必须是严格的当前树上重心。Tiw 太强力!

\(\mathcal{Code}\)

/* Clearink */

#include <cstdio>
#include <vector>
#include <algorithm> #define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i ) inline int rint() {
int x = 0, f = 1, s = getchar();
for ( ; s < '0' || '9' < s; s = getchar() ) f = s == '-' ? -f : f;
for ( ; '0' <= s && s <= '9'; s = getchar() ) x = x * 10 + ( s ^ '0' );
return x * f;
} template<typename Tp>
inline void wint( Tp x ) {
if ( x < 0 ) putchar( '-' ), x = -x;
if ( 9 < x ) wint( x / 10 );
putchar( x % 10 ^ '0' );
} inline void chkmax( int& a, const int b ) { a < b && ( a = b ); } const int MAXN = 2e5, IINF = 0x3f3f3f3f;
int n, ecnt, head[MAXN + 5], ans[MAXN + 5];
int siz[MAXN + 5], par[MAXN + 5]; // for global root.
struct Edge { int to, nxt; } graph[MAXN * 2 + 5];
bool vis[MAXN + 5]; inline void link( const int u, const int v ) {
graph[++ecnt] = { v, head[u] }, head[u] = ecnt;
graph[++ecnt] = { u, head[v] }, head[v] = ecnt;
} inline void findG( const int u, const int fa, int all, int& rt ) {
static int tsiz[MAXN + 5], wgt[MAXN + 5];
if ( !all ) all = tsiz[u];
tsiz[u] = 1, wgt[u] = 0;
for ( int i = head[u], v; i; i = graph[i].nxt ) {
if ( !vis[v = graph[i].to] && v != fa ) {
findG( v, u, all, rt ), tsiz[u] += tsiz[v];
chkmax( wgt[u], tsiz[v] );
}
}
chkmax( wgt[u], all - tsiz[u] );
if ( !rt || wgt[u] < wgt[rt] ) rt = u;
} inline void init( const int u ) {
siz[u] = 1;
for ( int i = head[u], v; i; i = graph[i].nxt ) {
if ( ( v = graph[i].to ) != par[u] ) {
par[v] = u, init( v );
siz[u] += siz[v];
}
}
} struct BinaryIndexTree {
int val[MAXN + 5];
inline void upd( int x, const int v ) {
for ( x = n - x + 1; x <= n; x += x & -x ) chkmax( val[x], v );
}
inline int ask( int x ) {
int ret = -IINF;
for ( x = n - x + 1; x; x -= x & -x ) chkmax( ret, val[x] );
return ret;
}
inline void clear( int x ) {
for ( x = n - x + 1; x <= n; x += x & -x ) val[x] = -IINF;
}
} bit; inline void update( const int u, const int fa, const int d, const int sr ) {
int su = u == par[fa] ? n - siz[u] : siz[u];
chkmax( ans[su << 1], bit.ask( su ) + d );
chkmax( ans[( su < sr ? su : sr ) << 1], d );
for ( int i = head[u], v; i; i = graph[i].nxt ) {
if ( !vis[v = graph[i].to] && v != fa ) {
update( v, u, d + 1, sr );
}
}
} inline void collect( const int u, const int fa, const int d ) {
int su = u == par[fa] ? n - siz[u] : siz[u];
bit.upd( su, d );
for ( int i = head[u], v; i; i = graph[i].nxt ) {
if ( !vis[v = graph[i].to] && v != fa ) {
collect( v, u, d + 1 );
}
}
} inline void clear( const int u, const int fa ) {
int su = u == par[fa] ? n - siz[u] : siz[u];
bit.clear( su );
for ( int i = head[u], v; i; i = graph[i].nxt ) {
if ( !vis[v = graph[i].to] && v != fa ) {
clear( v, u );
}
}
} inline void solve( const int u ) {
vis[u] = true; static std::vector<int> son;
son.clear();
for ( int i = head[u], v; i; i = graph[i].nxt ) {
if ( !vis[v = graph[i].to] ) {
son.push_back( v );
}
} for ( int v: son ) {
update( v, u, 1, par[v] == u ? siz[u] - siz[v] : siz[u] );
collect( v, u, 1 );
}
for ( int v: son ) clear( v, u );
std::reverse( son.begin(), son.end() );
for ( int v: son ) {
update( v, u, 1, par[v] == u ? siz[u] - siz[v] : siz[u] );
collect( v, u, 1 );
}
for ( int v: son ) clear( v, u ); for ( int i = head[u], v, rt; i; i = graph[i].nxt ) {
if ( !vis[v = graph[i].to] ) {
findG( v, u, 0, rt = 0 );
solve( rt );
}
}
} int main() {
n = rint();
rep ( i, 2, n ) link( rint(), rint() ); int rt = 0; findG( 1, 0, n, rt );
init( rt ); // let rt be global root.
rep ( i, 1, n ) bit.val[i] = -IINF; solve( rt ); per ( i, n - 1, 1 ) chkmax( ans[i], ans[i + 1] );
rep ( i, 1, n ) wint( i & 1 ? 1 : ans[i] + 1 ), putchar( '\n' );
return 0;
}

Solution -「JOISC 2021」「LOJ #3495」聚会 2的更多相关文章

  1. Solution -「JOISC 2021」「LOJ #3489」饮食区

    \(\mathcal{Description}\)   Link.   呐--不想概括题意,自己去读叭~ \(\mathcal{Solution}\)   如果仅有 1. 3. 操作,能不能做?    ...

  2. Solution -「JOISC 2021」「LOJ #3491」道路建设

    \(\mathcal{Description}\)   Link.   平面上有 \(n\) 个互不重合的点 \((x_{1..n},y_{1..n})\),求其两两曼哈顿距离的前 \(m\) 小值. ...

  3. Solution -「JOISC 2021」古老的机器

    \(\mathcal{Description}\)   Link.   这是一道通信题.   对于长度为一个 \(n\),仅包含字符 X, Y, Z 的字符串 \(s\),将其中 \(n\) 个字符按 ...

  4. Loj #2731 「JOISC 2016 Day 1」棋盘游戏

    Loj 2731 「JOISC 2016 Day 1」棋盘游戏 JOI 君有一个棋盘,棋盘上有 \(N\) 行 \(3\) 列 的格子.JOI 君有若干棋子,并想用它们来玩一个游戏.初始状态棋盘上至少 ...

  5. 【LOJ】#3036. 「JOISC 2019 Day3」指定城市

    LOJ#3036. 「JOISC 2019 Day3」指定城市 一个点的可以dp出来 两个点也可以dp出来 后面的就是在两个点的情况下选一条最长的链加进去,用线段树维护即可 #include < ...

  6. 【LOJ】#3034. 「JOISC 2019 Day2」两道料理

    LOJ#3034. 「JOISC 2019 Day2」两道料理 找出最大的\(y_{i}\)使得\(sumA_{i} + sumB_{y_i} \leq S_{i}\) 和最大的\(x_{j}\)使得 ...

  7. 【LOJ】#3032. 「JOISC 2019 Day1」馕

    LOJ#3032. 「JOISC 2019 Day1」馕 处理出每个人把馕切成N段,每一段快乐度相同,我们选择第一个排在最前的人分给他的第一段,然后再在未选取的的人中选一个第二个排在最前的切一下,并把 ...

  8. 【LOJ】#3033. 「JOISC 2019 Day2」两个天线

    LOJ#3033. 「JOISC 2019 Day2」两个天线 用后面的天线更新前面的天线,线段树上存历史版本的最大值 也就是线段树需要维护历史版本的最大值,后面的天线的标记中最大的那个和最小的那个, ...

  9. 【LOJ】#3031. 「JOISC 2019 Day1」聚会

    LOJ#3031. 「JOISC 2019 Day1」聚会 听说随机可过? 我想了很久想了一个不会被卡的做法,建出前\(u - 1\)个点的虚树,然后找第\(u\)个点的插入位置,就是每次找一条最长链 ...

随机推荐

  1. ElasticSearch的应用

    一.介绍 全文检索技术: 分布式: Restful风格: 近实时搜索 二.部署 下载:https://thans.cn/mirror/elasticsearch.html 新建用户,并登录: 解压: ...

  2. kafka学习笔记(二)kafka的基本使用

    概述 第一篇随笔从消息队列的定义和各种应用,以及kafka的分类定义和基本知识,第二篇就写一篇关于kafka的基本实际配置和使用的随笔,包括kafka的集群参数的配置,生产者使用机制,消费者使用机制. ...

  3. 简单Spring MVC项目搭建

    1.新建Project 开发环境我使用的是IDEA,其实使用什么都是大同小异的,关键是自己用的顺手. 首先,左上角File→New→Project.在Project页面选择Maven,然后勾上图中所示 ...

  4. OSI/RM体系结构

    OSI/RM体系结构是第一个标准化的计算机网络体系结构.   它是针对广域网通信(也就是不同网络之间的通信)进行设计 的,将整个网络通信的功能划分为七个层次,由低到高分别是物理层(Physical L ...

  5. JavaScript DOM 基础操作

    JavaScript DOM 基础操作 一.获取元素的六方式 document.getElementById('id名称') //根据id名称获取 document.getElementsByclas ...

  6. [C 语言基础] 如何调用不同文件中的函数

    很多时候需要将实现不同功能的函数或者与某个模块有关的函数写在一个文件里.这样有两个好处: 1. 方便以后调用:以后需要用到这个模块,或者这类函数,直接将相关文件复制过去,再稍微修改一下就能应用于不同场 ...

  7. 【C++】类-基础知识

    类-基础知识 目录 类-基础知识 1. 语法定义 2. 类的实现 3. 三个基本的函数 3.1 构造函数 功能 形式 调用时机 默认构造函数 3.2 复制构造函数 功能 形式 调用时机 3.3 析构函 ...

  8. Docker环境安装,基本命令集合

    一.docker安装 1).卸载旧的安装包 centos7默认安装的docker版本是1.13.1,卸载它,安装新的版本. root用户下,一次把这坨命令复制进去 yum remove docker ...

  9. 小程序循环时的item问题

    平常在做小程序时,比如循环渲染数据时,如果有多个数据层次,一般都会这样 wx:for-item=item2,它的意思只是简单的起了一个wx:for循环值的别名,不是表示循环item2,index2同理 ...

  10. golang中goroutine

    1. 概念 goroutine 奉行通过通信来共享内存,而不是共享内存来通信 goroutine 是由go的运行时(runtime)调度和管理的 go程序会智能的将goroutine中的任务合理的分配 ...