题解
首先考虑以一个点为根向外扩展会得到多少种不同的情形,显然以一个点为根的情况除了全部都染了应该都是不同的,所以情况数有以它为根时最深的点深度
?
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)
d1??dis(x,z)=d2??dis(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)
d?dis(w,z)=d1??dis(x,z),于此同时便有
d
?
d
i
s
(
x
,
z
)
=
d
1
?
d
i
s
(
x
,
y
)
d-dis(x,z)=d_1-dis(x,y)
d?dis(x,z)=d1??dis(x,y)与
d
?
d
i
s
(
y
,
z
)
=
d
2
?
d
i
s
(
x
,
y
)
d-dis(y,z)=d_2-dis(x,y)
d?dis(y,z)=d2??dis(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
d?hsec?+1。 这样就可以轻松地求出总的不同情况数了。
但我们再看看原题,原题要求不是所有点都可以做根。 如果没有这个条件是很容易用连通块的
∣
V
∣
?
∣
E
∣
|V|-|E|
∣V∣?∣E∣容斥解决的,但有了这个就不能这样做了,我们不好求出根只在某个连通块中的情况数。 但实际上我们可以考虑对于原来的
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;
}
谢谢!!!
|