Centroids (换根DP)
题面

题解
删一条边、加一条边,相当于把一个子树折下来,然后嫁接在一个点上,
那么最优的情况肯定是接在根上,对吧,很好理解吧
那么这个拆下来的子树大小就不能超过n/2。
我们用son[]来表示每个点为根的子树大小,
如果一个点x可以改造后变成重心,那么要么它本来就是重心,要么它最多只有一个儿子y的son[y]大于n/2,并且y的子树大小可以通过改造变得<=n/2。
要改造一个儿子的子树,最优的方法就是减去里面最大的小于等于n/2的子子树,我们用dp1[]来表示一个点的子树里这样一个子子树的大小,然后暴力判就是了。
由于每个点的父亲也要考虑,所以用换根DP,一个dp2[]表示对于除了点的子树以外的部分可以剪掉的最大的部分。
转移方程:
dp1[x] = max{son[y]*2 <= n ? son[y] : dp1[y]} //y 为 x 的儿子
dp2[x] = (n - son[x])*2 <= n ? (n - son[x]) : max(dp2[father[x]],max{son[y]*2 <= n ? son[y] : dp1[y]}) //y 为 x 的父亲除了 x 以外的其他儿子
/*
这里的dp2[x]后面不能暴力枚举y,只能处理出前缀最大值和后缀最大值来算
*/
注意,只能改造一次。
CODE
zxy A了! orz or2 orz
#include<cstdio>
#include<cstring>
#include<iostream>
//-----------F1
using namespace std;
#include<algorithm>
#include<cmath>
//-----------F2
#include<vector>
#include<stack>
#include<queue>
#include<map>
#define MAXN 400005
#define LL long long
#define lowbit(x) (-(x) & (x))
#define ENDL putchar('\n')
//#pragma GCC optimize(2)
//#pragma G++ optimize(3)
//#define int LL
char char_read_before = 1;
inline int read() {
int f = 1,x = 0;char s = char_read_before;
while(s < '0' || s > '9') {if(s == '-') f = -1;s = getchar();}
while(s >= '0' && s <= '9') {x = x * 10 - '0' + s;s = getchar();}
char_read_before = s;return x * f;
}
LL zxy = 1000000007ll; // 用来膜的
inline LL qkpow(LL a,LL b) {
LL res = 1;
while(b>0) {
if(b & 1) res = res * a % zxy;
a = a * a % zxy;
b >>= 1;
}
return res;
}
int n,m,i,j,s,o,k;
struct it{
int v,w;
it(){v=w=0;}
it(int V,int W){v = V;w = W;}
};
vector<int> g[MAXN];
vector<int> pre[MAXN],suf[MAXN];
int son[MAXN];
int dp1[MAXN],dp2[MAXN];
int ans[MAXN];
inline void dfs1(int x,int fa) {
dp1[x] = 0;
int sum = 0;
pre[x].push_back(0);
son[x] = 1;
stack<int> sf;
for(int i = 0;i < g[x].size();i ++) {
if(g[x][i] != fa) {
dfs1(g[x][i],x);
son[x] += son[g[x][i]];
dp1[x] = max(dp1[x],son[g[x][i]]*2 <= n ? son[g[x][i]] : dp1[g[x][i]]);
sum = max(sum,son[g[x][i]]*2 <= n ? son[g[x][i]] : dp1[g[x][i]]);
}
pre[x].push_back(sum);
}
sum = 0;
sf.push(0);
for(int i = (int)g[x].size() - 1;i >= 0;i --) {
if(g[x][i] != fa) {
sum = max(sum,son[g[x][i]]*2 <= n ? son[g[x][i]] : dp1[g[x][i]]);
}
sf.push(sum);
}
while(!sf.empty()) suf[x].push_back(sf.top()),sf.pop();
return ;
}
inline void dfs2(int x,int fa,int ll,int rr) {
ans[x] = 1;
int ct = 1;
if(fa) {
dp2[x] = (n - son[x])*2 <= n ? (n - son[x]) : max(dp2[fa],max(pre[fa][ll],suf[fa][rr]));
if((n - son[x])*2 > n) {
if((n - son[x] - dp2[x])*2 > n) ans[x] = 0;
ct = 0;
}
}
else dp2[x] = 0;
for(int i = 0;i < g[x].size();i ++) {
if(g[x][i] != fa) {
dfs2(g[x][i],x,i,i+1);
if(son[g[x][i]]*2 > n) {
if(ct == 0 || (son[g[x][i]] - dp1[g[x][i]])*2 > n) ans[x] = 0;
ct = 0;
}
}
}
return ;
}
signed main() {
n = read();
for(int i = 2;i <= n;i ++) {
s = read();o = read();
g[s].push_back(o);
g[o].push_back(s);
}
dfs1(1,0);
dfs2(1,0,0,0);
for(int i = 1;i <= n;i ++) {
printf("%d ",ans[i]);
}
return 0;
}
Centroids (换根DP)的更多相关文章
- [BZOJ4379][POI2015]Modernizacja autostrady[树的直径+换根dp]
题意 给定一棵 \(n\) 个节点的树,可以断掉一条边再连接任意两个点,询问新构成的树的直径的最小和最大值. \(n\leq 5\times 10^5\) . 分析 记断掉一条边之后两棵树的直径为 \ ...
- 2018.10.15 NOIP训练 水流成河(换根dp)
传送门 换根dp入门题. 貌似李煜东的书上讲过? 不记得了. 先推出以1为根时的答案. 然后考虑向儿子转移. 我们记f[p]f[p]f[p]表示原树中以ppp为根的子树的答案. g[p]g[p]g[p ...
- 换根DP+树的直径【洛谷P3761】 [TJOI2017]城市
P3761 [TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公 ...
- 小奇的仓库:换根dp
一道很好的换根dp题.考场上现场yy十分愉快 给定树,求每个点的到其它所有点的距离异或上m之后的值,n=100000,m<=16 只能线性复杂度求解,m又小得奇怪.或者带一个log像kx一样打一 ...
- 国家集训队 Crash 的文明世界(第二类斯特林数+换根dp)
题意 题目链接:https://www.luogu.org/problem/P4827 给定一棵 \(n\) 个节点的树和一个常数 \(k\) ,对于树上的每一个节点 \(i\) ,求出 \( ...
- Acesrc and Travel(2019年杭电多校第八场06+HDU6662+换根dp)
题目链接 传送门 题意 两个绝顶聪明的人在树上玩博弈,规则是轮流选择下一个要到达的点,每达到一个点时,先手和后手分别获得\(a_i,b_i\)(到达这个点时两个人都会获得)的权值,已经经过的点无法再次 ...
- bzoj 3566: [SHOI2014]概率充电器 数学期望+换根dp
题意:给定一颗树,树上每个点通电概率为 $q[i]$%,每条边通电的概率为 $p[i]$%,求期望充入电的点的个数. 期望在任何时候都具有线性性,所以可以分别求每个点通电的概率(这种情况下期望=概率 ...
- codeforces1156D 0-1-Tree 换根dp
题目传送门 题意: 给定一棵n个点的边权为0或1的树,一条合法的路径(x,y)(x≠y)满足,从x走到y,一旦经过边权为1的边,就不能再经过边权为0的边,求有多少边满足条件? 思路: 首先,这道题也可 ...
- [Bzoj3743][Coci2015] Kamp【换根Dp】
Online Judge:Bzoj3743 Label:换根Dp,维护最长/次长链 题目描述 一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的. 有K个人(分布在K个不同的 ...
随机推荐
- DevOps落地实践点滴和踩坑记录-(1)
记录初衷 本人一直在从事企业内DevOps落地实践的工作,走了不少弯路,也努力在想办法解决面临的问题,期间也经历过不少人和事情,最近突然有想法把经历过的,不管好的不好的都记录下来,分享给和我一样的一线 ...
- mac M1 php扩展 xlswriter 编译安装爬坑记录
电脑配置 MacBook Pro(14英寸,2021年) 系统版本 macOS Monterey 12.4 芯片 Apple M1 Pro PHP环境 MAMP Pro Version 6.6.1 ( ...
- python 基础知识-day10(面向对象)
1.面向对象的概念 拥有共同属性的一类进行归类的过程叫做面向对象. 2.注意事项 class定义类的时候,类名的首字母必须大写 3.面向对象案例 1 class Person(object): 2 d ...
- Elasticsearch 在地理信息空间索引的探索和演进
vivo 互联网服务器团队- Shuai Guangying 本文梳理了Elasticsearch对于数值索引实现方案的升级和优化思考,从2015年至今数值索引的方案经历了多个版本的迭代,实现思路从最 ...
- zabbix通过invoke调用监控服务可用性
1. 通过脚本判断线上服务是否可用 telnet 127.0.0.1 端口 #线上调用的是使用的dubbo端口 通过invoke 抓取返回的code值,脚本如下 #返回code:0则视为正常,返回其他 ...
- Linux shell脚本基础
程序的组成: 程序:算法+数据结构 数据:程序处理的目标 数据结构:相互之间存在一种或多种特定关系的数据元素的集合 算法:处理数据的方式 编程风格: 面向对象:把所有的操作都转化为对象的方式. 面向过 ...
- Mysql安装配置以及解决重装Mysql时忘记root password问题
目录 1.Mysql安装以及环境变量配置 重装Mysql时忘记root password问题 1.Mysql安装以及环境变量配置 官网安装:https://www.mysql.com/ 按 ...
- ArrayList和LinkedList内部是怎么实现的?他们之间的区别和优缺点?
ArrayList 内部使用了数组形式进行了存储,利用数组的下标进行元素的访问,因此对元素的随机访问速度非常快.因为是数组,所以ArrayList在初始化的时候, 有初始大小10,插入新元素的时候,会 ...
- CANN算子:利用迭代器高效实现Tensor数据切割分块处理
摘要:本文以Diagonal算子为例,介绍并详细讲解如何利用迭代器对n维Tensor进行基于位置坐标的大批量数据读取工作. 本文分享自华为云社区<CANN算子:利用迭代器高效实现Tensor数据 ...
- DDS信号发生器加强版(双通道,发送波形的频率可控,相位可控,种类可控)
目的:设计一个DDS,可以输出两个波形,输出的波形的周期可以修改,相位可以修改,种类也可以修改 输入:clk,reset,一个控制T的按键,一个控制相位的按键,一个控制波形种类的按键. 思路:双通道- ...