[AGC008F]Black Radius

Black Radius

题解

首先考虑以一个点为根向外扩展会得到多少种不同的情形,显然以一个点为根的情况除了全部都染了应该都是不同的,所以情况数有以它为根时最深的点深度 − 1 -1 1种。
但为什么我们不能将1其直接加起来作为总答案呢?当然是因为有几种情况它们得到的结果是一样的。
我们记 f ( i , j ) f(i,j) f(i,j)表示以点 i i i为根,将深度不超过 j j j的点全部染黑得到的树的状况。
可以发现,如果 f ( x , d 1 ) f(x,d_1) f(x,d1) f ( y , d 2 ) f(y,d_2) f(y,d2)的树状况一致,那么对于在 x , y x,y x,y的简单路径上的点 z z z,一定存在一个 d d d使得 f ( z , d ) = f ( x , d 1 ) = f ( y , d 2 ) f(z,d)=f(x,d_1)=f(y,d_2) f(z,d)=f(x,d1)=f(y,d2)
由于我们的 f ( x , d 1 ) f(x,d_1) f(x,d1) f ( y , d 2 ) f(y,d_2) f(y,d2)相同,那么一定有在 x , y x,y x,y的简单路径上,只有一个节点的子树不是全黑的,否则 x , y x,y x,y一定在其中一个子树上染色状况不同。
对于路径上的点 z z z,如果它的子树不是全黑,那么一定满足 d 1 − d i s ( x , z ) = d 2 − d i s ( y , z ) d_1-dis(x,z)=d_2-dis(y,z) d1dis(x,z)=d2dis(y,z),由于 d i s ( x , z ) + d i s ( y , z ) dis(x,z)+dis(y,z) dis(x,z)+dis(y,z)为定值 d i s ( x , y ) dis(x,y) dis(x,y)所以满足上面条件的 z z z d i s ( x , z ) dis(x,z) dis(x,z) d i s ( y , z ) dis(y,z) dis(y,z)是固定的,所以这样的 z z z是唯一的。
所以必然只有一个点的子树不是全黑的。
那么对于路径上的另一个点 w w w,只要满足 d − d i s ( w , z ) = d 1 − d i s ( x , z ) d-dis(w,z)=d_1-dis(x,z) ddis(w,z)=d1dis(x,z),于此同时便有 d − d i s ( x , z ) = d 1 − d i s ( x , y ) d-dis(x,z)=d_1-dis(x,y) ddis(x,z)=d1dis(x,y) d − d i s ( y , z ) = d 2 − d i s ( x , y ) d-dis(y,z)=d_2-dis(x,y) ddis(y,z)=d2dis(x,y)成立,于是我们一定可以得到相同的染色状况。
同时可以发现,这条路径上的点的 d d d值是呈现一个 V V V形的。
那么我们就可以说明所有可以得到相同树状况的点都是连通的,并且它们中 d d d最小的一定只有一个,同时相邻的点的 d d d值也是连续的。

我们不妨就在这个最小的 d d d上统计答案。
我们考虑一个点满足什么条件时它的 d d d一定是最小的。
我们先不看整棵树都是黑色的情况,那么以该点为根的最高的子树一定不是满的,而且如果我们的根不是唯一解,那么其它子树都应该是全黑的。
由于我们的 d d d值是呈 V V V形,所以除了最小值,其它点必然可以移动到一个 d d d值更小的点去。
显然,我们如果要让这个值更小,那么一定是向着最高的那一棵子树移动,这样我们的 d d d值显然是变小的。
当然,如果这个变小的 d d d值能够到达与原先一样的状态,那么它显然是必须将原来的其它子树都填满的。
于是原先的 d d d一定是得不小于次高子树的高度 + 2 +2 +2的,否则移动后就不满了。
那么如果我们当前值是最小的必要条件就有 d ⩽ h s e c + 1 d\leqslant h_{sec}+1 dhsec+1
这样就可以轻松地求出总的不同情况数了。

但我们再看看原题,原题要求不是所有点都可以做根。
如果没有这个条件是很容易用连通块的 ∣ V ∣ − ∣ E ∣ |V|-|E| VE容斥解决的,但有了这个就不能这样做了,我们不好求出根只在某个连通块中的情况数。
但实际上我们可以考虑对于原来的 d d d最小的点,找到一个最优秀的替代点,使其作为根时可以得到与原先相同的情况。
显然,这个替代点所在的子树必须是满的,如果在不满的子树的话,我也不可能向其移动,否则一定可以经过一个 d d d更小的点,那样就与我们原先的假设矛盾了。
但移动后我们肯定是有一个下限的,毕竟必须把移动到的那棵子树填满,那我们一定会选择像尽量满的子树移动,这样我们的下限会比较小,也就是涵盖所有合法的情况。

上面的方法很容易就可以用一个换根 d p dp dp实现,显然是线性的。
总时间复杂度 O ( n ) O\left(n\right) O(n)

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL; 
typedef long double ld;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const int mo=924844033;
const int mod=1e5+3;
const int inv2=5e8+4;
const int jzm=2333;
const int zero=2000;
const int lim=1500;
const int M=100000;
const int orG=3,ivG=332748118;
const long double Pi=acos(-1.0);
const double eps=1e-9;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,head[MAXN],tot,mxd[MAXN],cnt[MAXN],num;LL ans;
char str[MAXN];bool key[MAXN];
struct edge{int to,nxt;}e[MAXN<<1];
void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
void dosaka1(int u,int fa){
	mxd[u]=1;cnt[u]=key[u];
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==fa)continue;
		dosaka1(v,u);cnt[u]+=cnt[v];
		mxd[u]=max(mxd[v]+1,mxd[u]);
	}
}
void dosaka2(int u,int fa){
	int tmp1=1,tmp2=1,mxm=0,tp=n;if(key[u])tp=0;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(tmp1<=mxd[v])tmp2=tmp1,tmp1=mxd[v]+1,mxm=cnt[v];
		else if(tmp2<=mxd[v])tmp2=mxd[v]+1;
		if(cnt[v])tp=min(tp,mxd[v]);
	}
	if(mxm<num)ans+=min(tmp1-1,tmp2+1)-tp;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==fa)continue;
		cnt[u]-=cnt[v];cnt[v]+=cnt[u];
		mxd[u]=(mxd[v]+1==tmp1)?tmp2:tmp1;
		dosaka2(v,u);mxd[u]=tmp1;
		cnt[v]-=cnt[u];cnt[u]+=cnt[v];
	}
}
signed main(){
	read(n);
	for(int i=1;i<n;i++){
		int u,v;read(u);read(v);
		addEdge(u,v);addEdge(v,u);
	}
	scanf("%s",str+1);
	for(int i=1;i<=n;i++)key[i]=str[i]-'0',num+=key[i];
	dosaka1(1,0);dosaka2(1,0);if(num)ans++;
	printf("%lld\n",ans);
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值