题目
题意
有一棵树,每个点初始有黑白色,每次操作将一个同色连通块颜色翻转——至少多少次翻转能让整张图颜色相同。
思路
首先,可以想到:一开始可以将同色的连通块缩成一个点——这点很显然,就不说明了。
缩点后,得到一张黑白分层图。现在是在这张黑白分层图上得到答案。怎么做呢?给出一个结论——答案为缩点后图的直径加一除以二。即记直径为 d d d, A n s = ( d + 1 ) / 2 Ans=(d+1)/2 Ans=(d+1)/2
证明
现在考虑如何证明这个结论:懒得搞图,就不放图了。——刚刚本来还准备证一番, P P L \tt PPL PPL 突然告诉我:拿到直径,一直翻转直径中间这个点不就OK了?翻转次数为 ( d + 1 ) / 2 (d+1)/2 (d+1)/2也就不难理解了
Update
经 w y x \tt wyx wyx 的提醒才发现,并查集根本不需要,直接判颜色就行了。
Updated Code
vector<LL> G[MAXN];
bool Color[MAXN];
LL Fir[MAXN], Sec[MAXN], D[MAXN], Ans;
void GetZhi(LL x,LL Fa)
{
int l = G[x].size();
for (Int i = 0; i < l; ++ i)
{
LL to = G[x][i];
if (to == Fa)
continue;
GetZhi(to, x);
Ans = Max(Ans, D[x] + D[to] + (Color[x] != Color[to]));
D[x] = Max(D[x], D[to] + (Color[x] != Color[to]));
}
}
int main()
{
LL n;
read( n );
for (Int i = 1; i <= n; ++ i)
scanf("%d", &Color[i]);
for (Int i = 1; i < n; ++ i)
{
LL u, v;
read( u ); read( v );
G[u].push_back( v );
G[v].push_back( u );
}
GetZhi(1, 1);
printf("%lld", (Ans + 1) / 2);
return 0;
}
Code
LL Fa[MAXN];
vector<LL> G[MAXN];
inline void MakeSet(LL x)
{
for (Int i = 1; i <= x; ++ i)
Fa[i] = i;
}
LL FindSet(LL x)
{
if (Fa[x] != x)
Fa[x] = FindSet( Fa[x] );
return Fa[x];
}
inline void UnionSet(LL a,LL b)
{
LL u = FindSet( a );
LL v = FindSet( b );
if (u != v)
Fa[u] = v;
}
bool Color[MAXN];
void Merge(LL x,LL Fa)
{
int l = G[x].size();
for (Int i = 0; i < l; ++ i)
{
LL to = G[x][i];
if (to == Fa)
continue;
if (Color[to] == Color[x])
UnionSet(to, x);
Merge(to, x);
}
}
LL Fir[MAXN], Sec[MAXN], D[MAXN], Ans;
void GetZhi(LL x,LL Fa)
{
int l = G[x].size();
for (Int i = 0; i < l; ++ i)
{
LL to = G[x][i];
if (to == Fa)
continue;
GetZhi(to, x);
Ans = Max(Ans, D[x] + D[to] + (FindSet( x ) != FindSet( to )));
D[x] = Max(D[x], D[to] + (FindSet( x ) != FindSet( to )));
}
}
int main()
{
LL n;
read( n );
MakeSet( n );
for (Int i = 1; i <= n; ++ i)
scanf("%d", &Color[i]);
for (Int i = 1; i < n; ++ i)
{
LL u, v;
read( u ); read( v );
G[u].push_back( v );
G[v].push_back( u );
}
Merge(1, 1);
GetZhi(1, 1);
printf("%lld", (Ans + 1) / 2);
return 0;
}