[AGC008F]Black Radius

122 篇文章 0 订阅
98 篇文章 0 订阅

题目

传送门 to AtCoder

思路

这题真的很 🐮🍺,​可能就算是 10000 10000 10000 个我,在考场上也绝不可能想出来……

f ( x , d ) f(x,d) f(x,d) 表示,到 x x x 距离不超过 d d d 的点集,也就是染色方案。我最初考虑过计算 f ( x , d 1 ) = f ( y , d 2 ) f(x,d_1)=f(y,d_2) f(x,d1)=f(y,d2) 的情况,但我掐指一算:需要用 容斥,铁定完蛋。所以就没想了。

但正解就要这样做。因为这种方法比我高明得多。在此之前,我们先找找其性质。

譬如说,一个点 z z z 若满足 d 1 − dis ( x , z ) ≠ d 2 − dis ( y , z ) d_1-\text{dis}(x,z)\ne d_2-\text{dis}(y,z) d1dis(x,z)=d2dis(y,z),那么在 f ( x , d 1 ) f(x,d_1) f(x,d1) f ( y , d 2 ) f(y,d_2) f(y,d2) 中, z z z 的不含 x , y x,y x,y 的子树的被染黑深度是不同的。那怎么才可能满足 f ( x , d 1 ) = f ( y , d 2 ) f(x,d_1)=f(y,d_2) f(x,d1)=f(y,d2) 呢?那就是 z z z 的不含 x , y x,y x,y 的子树被 全部染黑。这个限制是很强的!

同时我们发现,若所有点都被染黑,总是重复。所以我们 假定所有点都被染黑是非法的;这无心一笔,却保证了 f ( x , d ) ≠ f ( x , d ′ )    ( d ≠ d ′ ) f(x,d)\ne f(x,d')\;(d\ne d') f(x,d)=f(x,d)(d=d) 恒成立,并且 必须存在 d 1 − dis ( x , z ) = d 2 − dis ( y , z ) d_1-\text{dis}(x,z)=d_2-\text{dis}(y,z) d1dis(x,z)=d2dis(y,z) 的点 z z z,因为只有它的子树可以不被全染黑,避免所有点都被染黑的尴尬。接下来,就直接说结论了:

f ( x , d 1 ) = f ( y , d 2 ) f(x,d_1)=f(y,d_2) f(x,d1)=f(y,d2),定义点 z z z 如上,记 d 0 = d 1 − dis ( x , z ) d_0=d_1-\text{dis}(x,z) d0=d1dis(x,z),则
∀ u ∈ Path ( x , y ) ,    f ( u , d 0 + dis ( u , z ) ) = f ( z , d 0 ) \forall u\in\text{Path}(x,y),\;f(u,d_0{\rm+}\text{dis}(u,z))=f(z,d_0) uPath(x,y),f(u,d0+dis(u,z))=f(z,d0)

也就是说, x , y x,y x,y 路径上的点都存在该染色方案,且所需距离恰好构成一个类似 y = ∣ x ∣ y=|x| y=x 图像的 V V V 型(点 z z z 为最低点)。这个结论是容易证明而难以发现的:考虑点 u ∈ Path ( x , y )    ( u ≠ z ) u\in\text{Path}(x,y)\;(u\ne z) uPath(x,y)(u=z),前面说过,它的不含 x , y x,y x,y 的子树必须被全部染黑;若 v ∈ Path ( x , y ) v\in\text{Path}(x,y) vPath(x,y) 要满足 f ( v , d v ) = f ( x , d 1 ) f(v,d_v)=f(x,d_1) f(v,dv)=f(x,d1),就要满足 d v ⩾ p u + dis ( u , v ) d_v\geqslant p_u+\text{dis}(u,v) dvpu+dis(u,v),其中 p u p_u pu u u u 的高度(叶子深度最大值)。这也是一个 V V V 型的限制!

再看 z z z 的限制:它的子树没被染黑,所以必须满足 d v = d 0 + dis ( z , v ) d_v=d_0+\text{dis}(z,v) dv=d0+dis(z,v) 。这是一个 V V V 型特解。这个解恰好经过端点 f ( x , d 1 ) f(x,d_1) f(x,d1) f ( y , d 2 ) f(y,d_2) f(y,d2),不难验证它必然满足所有 V V V 型限制,否则 f ( x , d 1 ) f(x,d_1) f(x,d1) f ( y , d 2 ) f(y,d_2) f(y,d2) 就已然不满足 V V V 型限制了。

结论得证后,对于任意一种染色方案 S    ( S ≠ U ) S\;(S\ne\Bbb U) S(S=U),假设我们把所有 f ( x , d ) = S f(x,d)=S f(x,d)=S 的点 x x x 标注出来,一定 构成连通块。我们已经有点想用 ∣ V ∣ − ∣ E ∣ = 1 |V|-|E|=1 VE=1 来直接容斥了;其实还可以更进一步。由于 f ( x , d 1 ) ≠ f ( x , d 2 )    ( d 1 ≠ d 2 ) f(x,d_1)\ne f(x,d_2)\;(d_1\ne d_2) f(x,d1)=f(x,d2)(d1=d2),所以一个 x x x 对应唯一的 d d d 。任意两个点之间的路径,其 d d d 值都构成 V V V 型,这说明相邻的点对应的 d d d 值不同,且没有一个点的 d d d 值比周围都高。于是可以验证:该连通块的 d d d只存在一个局部最小值,那就是全局最小值。

所以,我们 只在局部最小值上计算贡献,即只统计 f ( x , d ) f(x,d) f(x,d) 满足 ∀ ⟨ x , y ⟩ ∈ E ,    f ( y , d − 1 ) ≠ f ( x , d ) \forall\langle x,y\rangle\in E,\;f(y,d{\rm-}1)\ne f(x,d) x,yE,f(y,d1)=f(x,d) 的情况。这是 f ( x , d 1 ) = f ( y , d 2 ) f(x,d_1)=f(y,d_2) f(x,d1)=f(y,d2) 的简化版本:若 y ∉ y\notin y/ 最高的子树,则最高的子树必须被全染黑;最高的子树被全染黑,整棵树就全黑了,非法。故 y ∈ y\in y 最高子树,此时显然有 ( d − 2 ) < h s e c (d-2)<h_{sec} (d2)<hsec,这里 h s e c h_{sec} hsec 表示次高子树的高度。用 d p \tt dp dp 很容易实现。

接下来的问题是,我们只能选取关键点。也就是说,我们要判断,何时 f ( x , d ) f(x,d) f(x,d) 可以用 f ( y , d 0 )    ( y ∈ S ) f(y,d_0)\;(y\in S) f(y,d0)(yS) 表示呢?由于 d d d 是局部最小值,所以对 x , y x,y x,y 运用 结论 时,只会取 z = x z=x z=x 。也就是说只有 x x x 不包含 y y y 的子树可以未被全部染黑;而 z = x z=x z=x 又保证了这是充分的。所以,对于一个 x x x,我们找到其含关键点的子树,这些子树的高度取 min ⁡ \min min 就是 d x d_x dx 的下界!

这东西就需要换根 d p \tt dp dp 了;但终究是简单的。时间复杂度 O ( n ) \mathcal O(n) O(n)

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
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;
}

const int MAXN = 200005;
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 ++;
}

const int INF = 0x3fffffff;
struct Node{
	int first, second;
	int& operator[](const int &x){
		return x ? second : first;
	}
	void add(const int &x){
		if(x > first) second = first, first = x;
		else if(x > second) second = x; // getMax
	}
};
Node node[MAXN]; char str[MAXN];
int siz[MAXN], mn[MAXN];
void dfs(int x,int pre){
	if(str[x] == '0') mn[x] = INF;
	else mn[x] = 0, siz[x] = 1;
	for(int i=head[x]; ~i; i=e[i].nxt){
		if(e[i].to == pre) continue;
		dfs(e[i].to,x); siz[x] += siz[e[i].to];
		node[x].add(node[e[i].to][0]+1);
		if(siz[e[i].to]) // qualified subtree
			mn[x] = min(mn[x],node[e[i].to][0]+1);
	}
}

llong ans;
void reroot(int x,int pre){
	int mx = min(node[x][0],node[x][1]+2);
	if(mx > mn[x]) ans += mx-mn[x];
	for(int i=head[x]; ~i; i=e[i].nxt){
		if(e[i].to == pre) continue;
		const int son = (node[x][0] == node[e[i].to][0]+1);
		int dis = node[x][son]+1; node[e[i].to].add(dis);
		if(siz[e[i].to] < siz[1]) // x is qualified
			mn[e[i].to] = min(mn[e[i].to],dis);
		reroot(e[i].to,x);
	}
}

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);
	}
	scanf("%s",str+1);
	dfs(1,-1), reroot(1,-1);
	printf("%lld\n",ans+1);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值