0
点赞
收藏
分享

微信扫一扫

[AGC009D]Uninity

耳一文 2022-02-13 阅读 28

题目

传送门 to AtCoder

思路

对于我来说,超级大难题,完全没思路;对 H I D \sf HID HID 来说,垃圾小水题,随便做出来。

题目初步转化一下:构建一棵类点分树,使得高度最小。如果自顶向下建树,则有结论 a n s ∈ { k ,    k + 1 } ans\in\{k,\;k{\rm+}1\} ans{k,k+1},然而完全没用。也可考虑找重心的调整法——显然不行。

我又考虑 自底向上 建树。原树的叶子,在点分树上也一定是叶子。于是,与原树的叶子相邻的,是点分树的倒数第二层。但是再往上呢?我感觉,如果可以是 1 1 1 那就是 1 1 1 。或许可证;那么再往上呢?由于它并不形式化,我终究失败了。

本题要做的,就是 将上述思考写成定理所以又是结论题是吧。只从上面这种形象化的描述的角度来讲,很难阐明一些东西。

定理:设 f : V ↦ N f:V\mapsto\N f:VN,即给每个点标号,满足对于任意 f ( x ) = f ( y )    ( x ≠ y ) f(x)=f(y)\;(x\ne y) f(x)=f(y)(x=y),存在 z ∈ path ( x , y ) z\in\text{path}(x,y) zpath(x,y) 使得 f ( z ) > f ( x ) f(z)>f(x) f(z)>f(x),记 v ( f ) = max ⁡ f ( x ) v(f)=\max f(x) v(f)=maxf(x),则答案为 min ⁡ v ( f ) \min v(f) minv(f)

显然任意合法解都对应这样一个 f f f 。接下来只需要证明,这样的 f f f 又可以反过来构造一组解,且这个解的答案不超过 v ( f ) v(f) v(f) 。利用归纳法,一个点时显然成立;多个点时,找到 max ⁡ f ( x ) \max f(x) maxf(x),显然这是唯一的。将其作为点分树的根,对每个连通块施加归纳,其答案不超过 v ( f ′ ) ⩽ v ( f ) − 1 v(f')\leqslant v(f)-1 v(f)v(f)1,于是这个方案的答案不超过 v ( f ′ ) + 1 ⩽ v ( f ) v(f')+1\leqslant v(f) v(f)+1v(f),证毕。

然后,我们可以 贪心地递归处理。其来源,就是自底向上建树的想法,但这里我们可以给出更简洁、更形式化的证明。设 y < f ( x ) y<f(x) y<f(x),若令 f ( x ) = y f(x)=y f(x)=y x x x 的子树仍然是合法点分树(只考虑子树内的点时,满足定理的 f f f 映射),设 z z z x x x 的树上父亲,则再令 f ( z ) = max ⁡ [ f ( z ) , f ( x ) ] f(z)=\max[f(z),f(x)] f(z)=max[f(z),f(x)] 即可得到新的合法点分树(这里的 f ( x ) f(x) f(x) 是原映射值)。

验证该方案的合法性是容易的:跨过 x x x 的路径,如果跨过 z z z 就仍然合法,因为 f ( z ) f(z) f(z) 不小于原来的 f ( x ) f(x) f(x);不跨过 z z z 也仍然合法,这是前提条件。不跨过 x x x 的路径,必定仍合法。

v ( f ) v(f) v(f) 不会变。所以 f ( x ) f(x) f(x) 的取值是唯一的,就是恰好使得子树成为合法点分树的最小值。那么,在原树上 d f s \tt dfs dfs,存储每个子树内哪些 f ( x ) f(x) f(x) 是需要避开的(冇更大的 f f f 将其 “遮挡” 住)即可。考虑到 f ( x ) ⩽ log ⁡ n f(x)\leqslant\log n f(x)logn,可以状压,时间复杂度 O ( n ) \mathcal O(n) O(n)

代码

#include <cstdio> // JZM yydJUNK!!!
#include <iostream> // XJX yyds!!!
#include <algorithm> // XYX yydLONELY!!!
#include <cstring> // (the STRONG long for LONELINESS)
#include <cctype> // ZXY yydSISTER!!!
#include <vector>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
inline void getMin(int &x, const int &y){
	if(y < x) x = y;
}

const int MAXN = 100005;
struct Edge{
	int to, nxt;
	Edge() = default;
	Edge(int _t, int _n): to(_t), nxt(_n){}
};
Edge e[MAXN<<1]; int head[MAXN], cntEdge;
void addEdge(int a, int b){
	e[cntEdge] = Edge(b,head[a]), head[a] = cntEdge ++;
}

int ans;
int dfs(int x, int pre){
	int s = 0, bad = 0;
	for(int i=head[x]; ~i; i=e[i].nxt){
		if((i^1) == pre) continue;
		const int &&ch = dfs(e[i].to,i);
		bad |= s&ch, s |= ch;
	}
	int chose = bad ? 32-__builtin_clz(bad) : 0;
	chose += __builtin_ffs((s>>chose)+1)-1;
	ans = max(ans,chose); return ((s>>chose)|1)<<chose;
}

int main(){
	int n = readint();
	memset(head+1,-1,n<<2);
	for(int i=1,a,b; i!=n; ++i){
		a = readint(), b = readint();
		addEdge(a,b), addEdge(b,a);
	}
	dfs(1,-1); printf("%d\n",ans);
	return 0;
}
举报

相关推荐

0 条评论