Skip to content

题解-CF734F

Posted on:2023年8月9日 at 19:07

本文章遵守知识共享协议 CC-BY-NC-SA,同步发表于洛谷题解区,转载时须在文章的任一位置附上原文链接和作者署名(rickyxrc)。推荐在我的个人博客阅读。

一道……构造题?

题目大意

有一个数列 AA,通过 AA 构造出数列 BBCC,其中 Bi=k=1nAiandAkB_i = \sum_{k=1}^n A_i \operatorname{and} A_kCi=k=1nAiorAkC_i = \sum_{k=1}^n A_i \operatorname{or} A_k,现在给定 BBCC,要求你给出任意一组合法的 AA 或报告无解。

解题思路

要解这个题,我们首先要观察到这样一个性质:

aorb=i=02imax(ai,bi),aandb=i=02imin(ai,bi)a \operatorname{or} b = \sum_{i=0}2^i \max(a_i,b_i),a \operatorname{and} b = \sum_{i=0}2^i\min(a_i,b_i)aia_ibib_i 是数的二进制拆分低 ii 位)

那我们不难发现:

max(a,b)+min(a,b)=a+baorb+aandb=a+b\begin{aligned}\max(a,b) + \min(a,b) &= a+b \\ a \operatorname{or} b + a \operatorname{and} b &= a+b\end{aligned}

插一嘴:这里和 gcd(a,b)lcm(a,b)=ab\gcd(a,b)\operatorname{lcm}(a,b) = ab 有异曲同工之妙。

回归正题,我们将 BBCC 逐项相加,得出来的式子如下:

(B+C)i=k=1ai+ak=naiaAa\begin{aligned}(B+C)_i &= \sum_{k=1} a_i + a_k \\ &= na_i \sum_{a \in A}a \end{aligned}

于是我们可以将所有的项加起来:

i=1n(B+C)i=i=1nnaiaAa=naAa+naAa=2naAa\begin{aligned}\sum_{i=1}^n(B+C)_i &= \sum_{i=1}^n na_i\sum_{a\in A}a \\ &= n\sum_{a\in A}a + n\sum_{a\in A}a \\ &= 2n \sum_{a\in A}a \end{aligned}

我们就得到了每一项的总和,将上面构造的项依次减去它即可。

无解的情况就是要除的部分整除不了。

还有就是,在构造出原序列之后,我们还需要通过原序列构造出 AA 数列,来二次检验答案的正确性(hack:1 3 5)。

那我们如何快速计算呢?这里考虑按位计算贡献。当且仅当 aabb 的二进制第 ii 位同为 11 时才会对答案造成 2i2^i 的贡献。

所以我们按位统计贡献即可,复杂度线对。

#include <stdio.h>
#include <vector>

typedef long long i64;
const i64 mod = 1, maxn = 200007;

i64 li[maxn], n, u, v, diff, flg1, d, c1, sum, cnt[65], a[maxn], b[maxn], c[maxn];

int main()
{
    scanf("%d", &n);

    for (int i = 1; i <= n; i++)
        scanf("%d", &u), a[i] += u, li[i] += u;
    for (int i = 1; i <= n; i++)
        scanf("%d", &u), b[i] += u, li[i] += u;
    for (int i = 1; i <= n; i++)
        sum += li[i];

    if (sum % (2 * n))
        return puts("-1") & 0;
    sum /= (2 * n);

    for (int i = 1; i <= n; i++)
        if ((li[i] - sum) % n)
            return puts("-1") & 0;
        else
            c[i] = (li[i] - sum) / n;

    for (int i = 1; i <= n; i++)
        for (int bit = 0; bit < 30; bit++)
            if (c[i] & (1ll << bit))
                cnt[bit]++;

    for (int i = 1; i <= n; i++)
    {
        i64 res = 0;
        for (int bit = 0; bit < 30; bit++)
            if (c[i] & (1ll << bit))
                res += (1ll << bit) * cnt[bit];
        if (res != a[i])
            return puts("-1") & 0;
    }

    for (int i = 1; i <= n; i++)
        printf("%d ", c[i]);

    return 0;
}


在 Rickyxrc's blog 出现的文章,若无特殊注明,均采用 CC BY-NC-SA 4.0 协议共享,也就是转载时需要注明本文章的地址,并且引用本文章的文章也要使用相同的方式共享。