Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Determining the parity of NAND tree contraction outcomes

Tech 2

When computing the parity of a counting problem, a comon technique is to consider swapping two elements. If the first two edges selected do not intersect, swapping thier order does not chenge the result. Only intersecting cases must be considered.

For adjacent edges y---x---z, contracting in the two possible orders yields NAND(NAND(y,x),z) and NAND(y,NAND(x,z)). When y = z the two results are equivalent. When y ≠ z, they differ.

This leads to a key property: we can modify the operation to always pick a vertex and contract as shown below without changing the answer:

  \       |      /                       \ | /
--- x --- y --- z ---        =>        --- x ---
  /       |      \                       / | \

Consider the case with an even number of edges. A node is called good if its value is 1 and there is an odd number of ways for it to be the final surviving node. The answer is the number of good nodes.

To check whether a node x is good, root the tree at x and assume every operation involves the root (otherwise the two directions are symmetric).

For an odd number of edges, enumerate the first operation to reduce to the even case.

This process corresponds to counting topological orders of a tree obtained by merging edges into a perfect matching. Each matched pair of nodes is contracted into a single node, and the answer is equal to the number of topological orders of the resulting tree.

#include <bits/stdc++.h>

using namespace std;

#define ctz(x) (!(x)?0:__builtin_ctz((x)))

const int N = 505;

inline int read() {
    register int s = 0, f = 1; register char ch = getchar();
    while (!isdigit(ch)) f = (ch == '-' ? -1 : 1), ch = getchar();
    while (isdigit(ch)) s = (s * 10) + (ch & 15), ch = getchar();
    return s * f;
}

set<int> e[N], t[N];

int id[N], val[N], a[N], n;

inline void shrink(int u, int v) {
    if (u > v) swap(u, v);
    for (int i = 1; i < v; ++i) id[i] = i; id[v] = u;
    for (int i = v + 1; i <= n; ++i) id[i] = i - 1;
    for (int i = 1; i <= n; ++i) val[id[i]] = a[i], t[i].clear();
    val[id[u]] = !(a[u] & a[v]);
    for (int i = 1; i <= n; ++i) {
        for (int j : e[i])
            if (id[i] != id[j])
                t[id[i]].insert(id[j]);
    }
}

int siz[N];
bool par[N];

inline int dfs(int now, int fa) {
    siz[now] = 1;
    for (int i : t[now])
        if (i != fa) {
            int v = dfs(i, now);
            if (v == -1) return -1;
            if (v) {
                if (!par[now]) par[now] = 1;
                else return -1;
            }
            siz[now] += siz[i];
        }
    return par[now] ^ 1;
}

inline int calc(int n) {
    int res = 0, fn = 0;
    for (int i = 1; i <= n / 2; ++i) fn += ctz(i);
    for (int i = 1; i <= n; ++i) {
        if (!val[i]) continue;
        for (int j = 1; j <= n; ++j) par[j] = 0;
        if (dfs(i, 0) == -1) continue;
        int cnt = fn;
        for (int j = 1; j <= n; ++j)
            if (par[j]) cnt -= ctz(siz[j] / 2);
        res ^= (cnt == 0);
    } return res;
}

int main() {
    n = read();
    for (int i = 1, u, v; i < n; ++i) {
        u = read(); v = read();
        e[u].insert(v);
        e[v].insert(u);
    }
    for (int i = 1; i <= n; ++i) a[i] = read();
    if (n & 1) {
        for (int i = 1; i <= n; ++i)
            t[i] = e[i], val[i] = a[i];
        printf("%d\n", calc(n));
    } else {
        int res = 0;
        for (int i = 1; i <= n; ++i)
            for (int j : e[i])
                if (i < j) {
                    shrink(i, j);
                    res ^= calc(n - 1);
                }
        printf("%d\n", res);
    }
    return 0;
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.