P9039 [PA2021] Drzewo czerwono-czarne
题意
给定一棵树,最初每个节点有一个颜色 \(0 / 1\)。
给定一个结束序列,你需要判断是否可以通过任意次数的下列操作,使树的每个节点的颜色与该序列一一对应。
- 选择一条边 \(u \to v\),使 \(u\) 和 \(v\) 都覆盖上 \(u\) 的颜色。
Sol
其实是弱智分讨题。
首先考虑链的情况,注意到一个显然的事情。
我们需要对于链的颜色段数进行讨论,显然每一段可以向左右不断延申,也可以将某一段给吞掉。
所以颜色段数显然不会变多,特判掉第一段,直接判掉即可。
不难通过打个暴力、集中注意力、意淫,等方式,注意到若该树不是一条链,显然有解。
考虑证明。
首先先判掉一些显然的东西:
- 开始与结束相等
- 开始序列只有一种颜色
- 结束序列没有相同的边
不难注意到对于一个度数为 \(3\) 的点 \(u\)。
考虑将 \(u\) 转到根上面。
发现对于任意时刻,对于 \(u\) 的所有儿子 \(v\),都可以在其中两个点上,分别储存两种颜色。
此时,我们不需要关心 \(u\) 的颜色,可以先将另一个儿子的子树暴力染掉。
然后再使用另一个儿子储存一种颜色,去染下一个儿子。
最后,使用最后一个儿子,染掉 \(u\) 即可。
Code
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <array>
#include <assert.h>
using namespace std;
#ifdef ONLINE_JUDGE
/* #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++) */
/* char buf[1 << 23], *p1 = buf, *p2 = buf, ubuf[1 << 23], *u = ubuf; */
#endif
int read() {
int p = 0, flg = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') flg = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
p = p * 10 + c - '0';
c = getchar();
}
return p * flg;
}
void write(int x) {
if (x < 0) {
x = -x;
putchar('-');
}
if (x > 9) {
write(x / 10);
}
putchar(x % 10 + '0');
}
bool _stmer;
const int N = 1e5 + 5, M = 2e5 + 5;
namespace G {
array <int, N> fir;
array <int, M> nex, to;
int cnt = 1;
void add(int x, int y) {
cnt++;
nex[cnt] = fir[x];
to[cnt] = y;
fir[x] = cnt;
}
} //namespace G
string s1, s2;
char strbuf[N];
array <int, N> fa, deg;
void dfs(int x, int &tp1, int &tp2) {
if (s1[x] != s1[fa[x]]) tp1++;
if (s2[x] != s2[fa[x]]) tp2++;
for (int i = G::fir[x]; i; i = G::nex[i])
if (fa[x] != G::to[i])
fa[G::to[i]] = x, dfs(G::to[i], tp1, tp2);
}
void solve() {
int n = read();
G::cnt = 1;
scanf("%s", strbuf), s1 = strbuf;
scanf("%s", strbuf), s2 = strbuf;
s1 = " " + s1, s2 = " " + s2;
bool flg1 = 1, flg2 = 1;
for (int i = 1; i <= n; i++) deg[i] = fa[i] = G::fir[i] = 0;
for (int i = 1; i <= n; i++)
flg1 &= s1[i] == s2[i], flg2 &= i == 1 || s1[i] == s1[i - 1];
int rt = 0;
bool flg3 = 1, flg4 = 0;
for (int i = 2; i <= n; i++) {
int x = read(), y = read();
G::add(x, y), G::add(y, x);
deg[x]++, deg[y]++, flg3 &= !(s2[x] == s2[y]);
}
for (int i = 1; i <= n; i++) {
if (deg[i] > 2) flg4 = 1;
if (deg[i] == 1) rt = i;
}
if (flg1) return (void)puts("TAK");
if (flg2) return (void)puts("NIE");
if (flg3) return (void)puts("NIE");
if (flg4) return (void)puts("TAK");
assert(rt);
int tp1 = 0, tp2 = 0;
dfs(rt, tp1, tp2);
if (s1[rt] != s2[rt]) tp1--;
if (tp1 >= tp2) return (void)puts("TAK");
else return (void)puts("NIE");
}
bool _edmer;
int main() {
cerr <<(&_stmer - &_edmer) / 1024.0 / 1024.0 << "MB\n";
int T = read();
while (T--) solve();
return 0;
}