CF 1552D. Array Differentiation


Array Differentiation

题意

给定长度为 \(n\) 的序列 \(a\) ,问是否存在一个长度为 \(n\) 的序列,满足:

  • 对于任意 \(1 \le i \le n\),存在 \(1 \le j, k \le n\) ,满足 \(a_i = b_j - b_k\)
    其中 \(1 \le n \le 10\)

分析

\(b_i\) 当作点权, \(a_i\) 当作边权,对于 \(a_i = b_j - b_k\)\(b_j\)\(b_k\) 连一条边。

\(n\) 个点内,我们可以构造使得 \(a\) 序列前 \(n-1\) 个数字被满足,比如令 \(b_{i+1} - b_i = a_i\)
这样就形成了一个链。那么,对于剩下一个元素,我们想让它也满足,那么必然会形成一个环。
对于首尾相连的环, \((b_{i+1} - b_i) + (b_i - b_{i-1}) + ... + (b_2 - b_1) + (b_1 - b_{i+1}) = 0\) ,而 \(a_i = b_{i+1} - b_i\),也就是这个环上所有边权相加为 \(0\) 。但是原始环在有方向时不一定是环,所以要重排方向,这样每个边权都可能有两种状态。

所以我们只需要找出是否存在这样的环即可,对于每个点,都有三种状态:

  1. 不在环内。
  2. 在环中,重排后为正数。
  3. 在环中,重排后为负数。

用三进制数判断每个点的状态。

Code

#include 
using namespace std;

void solve ()
{
    int n, m = 1; cin >> n;
    vector a(n); forr(&x, a) cin >> x, m *= 3;

    for (int i = 1; i < m; i ++ )
    {
        int s = i, sum = 0;
        for (int j = 0; j < n; j ++ )
        {
            if (s % 3 == 1) sum += a[j];
            else if (s % 3 == 2) sum -= a[j];
            s /= 3;
        }
        if (!sum) return cout << "YES\n", void();
    }
    cout << "NO\n";
}

signed main ()
{
    cout.tie(0)->sync_with_stdio(0);
    int _; for (cin >> _; _--; ) solve();
    return 0;
}