\(\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. CentOS7防火墙firewalld 和 CentOS6防火墙iptables的一些配置命令

    CentOS7 防火墙 一.防火墙的开启.关闭.禁用.查看状态命令 (1)启动防火墙:systemctl start firewalld (2)关闭防火墙:systemctl stop firewal ...

  2. react中使用antd按需加载(第一部)

    什么是react按需加载?简单来说就是当我们引用antd的时候需要引入全局css样式,这会对性能造成一定的影响,那么使用按需加载以后就不需要引入css全局样式了,直接引入功能模块即可,既然需要设置按需 ...

  3. Servlet部署描述符

    注:图片如果损坏,点击文章链接:https://www.toutiao.com/i6512237744641540612/ <Servlet简单实现开发部署过程>中的过程,可以概括为以下模 ...

  4. 听说你想在 WordPress 网站上嵌入 PPT ?

    年底了,想在 WordPress 博客上展示自己的春节旅行计划,尝试在文章中插入一个旅行计划 PPT 结果长这个样子 你有没有遇到同样的情况,懊恼网页支持展示的内容无法满足我们的需求: 想展示年度家庭 ...

  5. Boost下载安装

    下载解压 官方地址 wget https://dl.bintray.com/boostorg/release/1.72.0/source/boost_1_72_0.tar.gz tar -zxvf b ...

  6. Java构造器(构造方法)

    类中的构造器也成为构造方法,是在进行创建对象的时候必须调用的,并且构造器有以下两个特点: 1.必须和类名字相同 2.必须没有返回类型也不能写void public class Demo06 { //一 ...

  7. 搭服务器之centos-ipv6源--配置各虚拟机系统的ipv6网络安装源。

    在2g内存的台式机里安装了三台虚拟机,跑起来好可以,就是swap用的比较多,图见上一篇随笔.现在平台基本有了,自己笔记本算总控,实验室台式机跑着4台机器(一实三虚),加上一台服务器,可以做很多事情了, ...

  8. vector自实现(一)

    vector.h: #ifndef __Vector__H__ #define __Vector__H__ typedef int Rank; #define DEFAULT_CAPACITY 3 t ...

  9. JavaWeb后端工程师技能图

  10. python3调用js的库之execjs

    执行JS的类库:execjs,PyV8,selenium,node execjs是一个比较好用且容易上手的类库(支持py2,与py3),支持 JS runtime. 1.安装: pip install ...