「雅礼集训 2017 Day7」跳蚤王国的宰相(树的重心)
题面
「
雅
礼
集
训
2017
D
a
y
7
」
跳
蚤
王
国
的
宰
相
传
统
2000
m
s
1024
M
i
B
{\tt「雅礼集训 2017 Day7」跳蚤王国的宰相}\\ \,_{传统~~~~~2000\,{\tt ms}~~~1024\,{\tt MiB}}
「雅礼集训2017Day7」跳蚤王国的宰相传统 2000ms 1024MiB
题目描述
跳蚤王国爆发了一场动乱,国王在镇压动乱的同时,需要在跳蚤国地方钦定一个人来做宰相。
由于当时形势的复杂性,很多跳蚤都并不想去做一个傀儡宰相,带着宰相的帽子,最后还冒着被打倒并杀头的危险,然而有一只跳蚤却想得与众不同最时尚。
本来他打算去教书,他已经发表了自己在学术方面的见解,获得了很多跳蚤们的赞同,但是这时听说跳蚤国要钦定宰相,他毅然打断了想去教书的想法,他觉得只要为了国家利益,自己的生死都可以不管,哪里能因为工作能给自己带来灾祸或者福分就去避开或者接近这份工作呢?所以他决定站出来接了这份工作。
然而当时国王的钦定方式很奇怪,跳蚤王国可以看作一棵树,国王认为宰相必须更好的为跳蚤服务,所以他会选择一个到所有节点距离和最小的节点,并在这个节点中钦定,如果有多个节点满足距离和最小则任选一个。
然而跳蚤国的动乱实在是太厉害了,以至于树的形态可能也会发生改变,也就是说,树上可能会有若干条边消失,如果这个情况出现的话一定会有同样数目的边出现,以保证整个结构仍然是一棵树。
现在这个跳蚤想知道每个节点中的跳蚤如果要被钦定,至少需要多少条边消失(当然也会有同样数目的边出现)。作为这只跳蚤的一名真正的粉丝,你能帮他解决这个问题吗?
输入格式
第一行一个正整数
n
\tt n
n 表示树中点的个数。
接下来
n
−
1
\tt n-1
n−1 行,每行两个正整数
u
\tt u
u、
v
\tt v
v,表示点
u
\tt u
u 与点
v
\tt v
v 之间有一条树边。
输出格式
输出
n
\tt n
n 行,第
i
\tt i
i 行一个数,表示第
i
\tt i
i 个节点如果要被钦定至少需要多少条边消失。
样例
输入
10
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
输出
0
4
4
4
4
4
4
4
4
4
数据范围与提示
对于
10
%
\tt10\%
10% 的数据,
n
≤
10
\rm n\leq10
n≤10;
对于
40
%
\tt40\%
40% 的数据,
n
≤
2000
\rm n\leq2000
n≤2000;
对于
70
%
\tt70\%
70% 的数据,
n
≤
100000
\rm n\leq100000
n≤100000;
对于
100
%
\tt100\%
100% 的数据,
10
≤
n
≤
1000000
\rm 10\leq n\leq1000000
10≤n≤1000000。
L
i
b
r
e
O
J
P
o
w
e
r
e
d
b
y
S
Y
Z
O
J
N
G
_{{\rm LibreOJ}~{\tt Powered~by}~{\rm SYZOJ~NG}}
LibreOJ Powered by SYZOJ NG
题解
首先,不难发现,到所有节点距离和最小的节点,就是树的重心。
用调整法证明,如果不是重心的话,往重心方向移动,一定能使到所有节点距离和减小。
然后,就可以利用很多重心的特性了。重心的定义是连出去的子树大小都不大于
n
/
2
\rm n/2
n/2 。
断边加边,实际上只需要考虑断边,然后断掉的部分肯定接在目标点上最优。
如果有两个重心,将会变得非常简单。两个重心一定是一条边相连的相邻的两个点,这条边的两端,各有
≤
n
/
2
\rm\leq n/2
≤n/2 个点。
对于本就是重心的点,答案是 0,对于其他点,只需要把两个重心之间的边断掉,然后把另一部分接在自己头上,就一定能满足要求了,因此答案是 1 。
如果只有一个重心,那我们就根据这个重心来考虑。设这个重心为
h
v
\rm hv
hv,以它为根处理一下这棵树每个点的
s
i
z
\rm siz
siz (子树大小)。
我们发现每个点只会有一个儿子的子树不符合要求,且
h
v
\rm hv
hv 一定在这个子树里。那么我们就只需要断掉不同的与
h
v
\rm hv
hv 相邻的边,就可以解决问题。如果不是断掉这些边,那么接上后肯定不会很优。
把与
h
v
\rm hv
hv 相邻的子树都处理出大小、成员,按大小排个序,然后处理出前缀和。假设当前的点
i
\rm i
i 所在的大子树为
p
\rm p
p ,那么我们需要从大到小考虑断掉一些子树,再接上去,保证这个点可以做重心,即断掉过后的
S
I
Z
−
s
i
z
[
i
]
≤
n
/
2
\rm SIZ-siz[i]\leq n/2
SIZ−siz[i]≤n/2。
既然从大到小考虑,那么除开
p
\rm p
p 本身的影响,选的应该是从大到小的连续一段,所以可以二分。
这里有一个坑点,困扰了我八个月。要使点
i
i
i 成为重心,可以不动子树
p
\rm p
p ,把其它的子树进行二分求出答案。也可以有第二种方案:把子树
p
\rm p
p 的那条边断掉,然后把
h
v
\rm hv
hv 加上除了
p
\rm p
p 以外的子树——这一大坨,全部接到
i
\rm i
i 上,接着再考虑二分、选其它的子树断掉,这样,再最终答案+1 的情况下,就只需要满足断掉后的
S
I
Z
−
s
i
z
[
p
]
≤
n
/
2
\rm SIZ-siz[p]\leq n/2
SIZ−siz[p]≤n/2 了,
s
i
z
[
p
]
\rm siz[p]
siz[p] 更大,条件要宽松些。
两种方案都试试,然后择优输出就是了。时间复杂度
O
(
n
log
n
)
\rm O(n\log n)
O(nlogn) 。
CODE
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 1000005
#define LL long long
#define DB double
#define ENDL putchar('\n')
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f * x;
}
const int MOD = 1000000007;
int n,m,i,j,s,o,k;
vector<int> g[MAXN];
int fa[MAXN],siz[MAXN];
bool hv[MAXN];
vector<int> sb[MAXN];
int b[MAXN];
void dfs(int x,int fat) {
fa[x] = fat;
siz[x] = 1;
hv[x] = 1;
for(int i = 0;i < (int)g[x].size();i ++) {
if(g[x][i] != fat) {
dfs(g[x][i],x);
if(siz[g[x][i]] > n/2) hv[x] = 0;
siz[x] += siz[g[x][i]];
}
}
if(n-siz[x] > n/2) hv[x] = 0;
return ;
}
void dfs2(int x,int fat,int id) {
sb[id].push_back(x);
siz[x] = 1;
for(int i = 0;i < (int)g[x].size();i ++) {
if(g[x][i] != fat) {
dfs2(g[x][i],x,id);
siz[x] += siz[g[x][i]];
}
}return ;
}
int SZ(int x) {return (int)sb[b[x]].size();}
vector<int> bu[MAXN];
int AS[MAXN],sm[MAXN],cnt,hp;
int SM(int l,int x) {
return sm[l] - (x >= l ? SZ(x):0);
}
int CNT(int l,int x) {
return cnt-l+1 - (x >= l ? 1:0);
}
int bel[MAXN];
int main() {
n = read();
for(int i = 1;i < n;i ++) {
s = read();o = read();
g[s].push_back(o);
g[o].push_back(s);
}
dfs(1,0);
cnt = 0,hp = 0;
for(int i = 1;i <= n;i ++) {
if(hv[i]) cnt ++,hp = i;
}
if(cnt > 1) {
for(int i = 1;i <= n;i ++) {
if(hv[i]) printf("0\n");
else printf("1\n");
}
return 0;
}
cnt = 0;
for(int i = 0;i < (int)g[hp].size();i ++) {
dfs2(g[hp][i],hp,++ cnt);
bu[(int)sb[cnt].size()].push_back(cnt);
}
cnt = 0;
for(int i = 1;i <= n;i ++) {
for(int j = 0;j < (int)bu[i].size();j ++) {
b[++ cnt] = bu[i][j];
}
}
sm[cnt+1] = 0;
for(int i = cnt;i > 0;i --) sm[i] = sm[i+1] + SZ(i);
for(int i = 1;i <= cnt;i ++) {
for(int j = 0;j < (int)sb[b[i]].size();j ++) {
int p = sb[b[i]][j];
int SIZ = siz[p];
int ad = 1,ad2 = 1;
for(int k = 20;k >= 0;k --) {
if(ad+(1<<k) <= cnt+1 && n-SIZ-SM(ad+(1<<k),i) <= n/2) {
ad += (1<<k);
}
}
for(int k = 20;k >= 0;k --) {
if(ad2+(1<<k) <= cnt+1 && n-SZ(i)-SM(ad2+(1<<k),i) <= n/2) {
ad2 += (1<<k);
}
}
AS[p] = min(CNT(ad,i),CNT(ad2,i)+1);
bel[p] = i;
}
}
for(int i = 1;i <= n;i ++) {
printf("%d\n",AS[i]);
}
return 0;
}
「雅礼集训 2017 Day7」跳蚤王国的宰相(树的重心)的更多相关文章
- 【思维题 细节】loj#6042. 「雅礼集训 2017 Day7」跳蚤王国的宰相
挂于±1的细节…… 题目描述 跳蚤王国爆发了一场动乱,国王在镇压动乱的同时,需要在跳蚤国地方钦定一个人来做宰相. 由于当时形势的复杂性,很多跳蚤都并不想去做一个傀儡宰相,带着宰相的帽子,最后还冒着被打 ...
- LOJ #6042. 「雅礼集训 2017 Day7」跳蚤王国的宰相
我可以大喊一声这就是个思博题吗? 首先如果你能快速把握题目的意思后,就会发现题目就是让你求出每个点要成为树的重心至少要嫁接多少边 先说一个显然的结论,重心的答案为\(0\)(废话) 然后我们考虑贪心处 ...
- 【LOJ6042】「雅礼集训 2017 Day7」跳蚤王国的宰相(思博题)
点此看题面 大致题意: 给你一棵树,询问对于每个点需要改变多少条边来使得它成为树中到所有点距离和最小的点. 一些初始化及想法 这是一道思博题. 首先我们要知道一个结论:对于这棵树的重心,它的答案必定为 ...
- 「雅礼集训 2017 Day7」事情的相似度
「雅礼集训 2017 Day7」事情的相似度 题目链接 我们先将字符串建后缀自动机.然后对于两个前缀\([1,i]\),\([1,j]\),他们的最长公共后缀长度就是他们在\(fail\)树上对应节点 ...
- 【LOJ 6041】「雅礼集训 2017 Day7」事情的相似度
Description 人的一生不仅要靠自我奋斗,还要考虑到历史的行程. 历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势. 你发现在历史的不同时刻,不断的 ...
- 【刷题】LOJ 6041 「雅礼集训 2017 Day7」事情的相似度
题目描述 人的一生不仅要靠自我奋斗,还要考虑到历史的行程. 历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势. 你发现在历史的不同时刻,不断的有相同的事情发 ...
- LOJ #6041. 「雅礼集训 2017 Day7」事情的相似度
我可以大喊一声这就是个套路题吗? 首先看到LCP问题,那么套路的想到SAM(SA的做法也有) LCP的长度是它们在parent树上的LCA(众所周知),所以我们考虑同时统计多个点之间的LCA对 树上问 ...
- loj#6041. 「雅礼集训 2017 Day7」事情的相似度(SAM set启发式合并 二维数点)
题意 题目链接 Sol 只会后缀数组+暴躁莫队套set\(n \sqrt{n} \log n\)但绝对跑不过去. 正解是SAM + set启发式合并 + 二维数点/ SAM + LCT 但是我只会第一 ...
- LOJ #6043. 「雅礼集训 2017 Day7」蛐蛐国的修墙方案
我可以大喊一声这就是个SB题吗? 首先讲一句如果你像神仙CXR一样精通搜索你就可以得到\(80pts\)(无Subtask)的好成绩 我们考虑挖掘一下题目的性质,首先发现这是一个置换,那么我们发现这的 ...
随机推荐
- neo4j中重复节点问题
neo4j中重复节点问题 neo4j中,在导入csv文件时,可能会出现有重复节点的现象.可以分为以下步骤解决:1.先查看下某个标签下的节点总数match (n:PERSON) return count ...
- 【Java面试】TCP协议为什么要设计三次握手?
一个工作5年的粉丝,最近去面试了很多公司,每次都被各种技术原理题问得语无伦次. 由于找了快1个月时间的工作,有点焦虑,来向我求助. 我能做的只是保证每天更新一个面试题,然后问他印象最深刻的一个面试题是 ...
- Pytorch实现波阻抗反演
Pytorch实现波阻抗反演 1 引言 地震波阻抗反演是在勘探与开发期间进行储层预测的一项关键技术.地震波阻抗反演可消除子波影响,仅留下反射系数,再通过反射系数计算出能表征地层物性变化的物理参数.常用 ...
- 微服务探索之路04篇k8s增加子节点,metrics资源监控,ingress-nginx域名配置及https配置
1 k8s增加子节点 1.1 子节点服务器安装docker,使用脚本自动安装 curl -fsSL https://get.docker.com | bash -s docker --mirror A ...
- ES5的继承和ES6的继承有什么区别?让Babel来告诉你
如果以前问我ES5的继承和ES6的继承有什么区别,我一定会自信的说没有区别,不过是语法糖而已,充其量也就是写法有区别,但是现在我会假装思考一下,然后说虽然只是语法糖,但也是有点小区别的,那么具体有什么 ...
- idea 在创建maven时没有src的解决方法
在创建maven时 加上archetypeCatalog=internal
- Cayley 定理与扩展 Cayley 定理
Cayley 定理 节点个数为 \(n\) 的无根标号树的个数为 \(n^{n−2}\) . 这个结论在很多计数类题目中出现,要证明它首先需要了解 \(\text{Prufer}\) 序列的相关内容. ...
- 浅学hello world
Hello world 1.随便新建一个文件夹,存放代码 2.新建一个java文件 .后缀名为.java .Hello.java .[注意点]系统没显示后缀名的可以自己手动打开 3.编写代码 publ ...
- 研发效能生态完整图谱&DevOps工具选型必看
本文主要梳理了研发效能领域完整的方向图谱以及主流工具,其中对少部分工具也做了一些点评.看了之后,大家可以对研发效能这个领域有个整体认识,同时研发效能落地的时候也有对应的工具(黑话叫抓手)可以选择. 我 ...
- MySQL--用通配符进行过滤(LIKE操作符)
1.LIKE操作符 怎样搜索产品名中包含文本anvil的所有产品?用简单的比较操作符肯定不行,必须使用通配符.利用通配符可创建比较特定数据的搜索模式.在这个例子中,如果你想找出名称包含anvil的所有 ...