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;
}
posted @ 2024-04-11 20:31  cxqghzj  阅读(16)  评论(0编辑  收藏  举报