疑惑 题解

Posted Little-岸芷汀兰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了疑惑 题解相关的知识,希望对你有一定的参考价值。

一、题目:


二、思路:

这道题 Robert_JYH 说是一道简单题,在这里向他致以最崇高的膜拜。

要做出来这道题,我们首先得明白亦或的本质,即“相同与不同”。相同就是0,不同就是1。

现在来考虑这样一个性质:设 \\(\\{a_n\\}\\) 是一个从小到大排好序的序列。那么 \\(\\min\\limits_{i,j}\\{a_i\\oplus a_j\\}=\\min\\limits_{1\\leq i<n}\\{a_i\\oplus a_{i+1}\\}\\),即序列中两个元素亦或起来的最小值一定产生于相邻的两个元素之间。

要说明这个性质成立,我们只需说明 \\(\\forall 0\\leq x<y<z\\),一定有 \\(x\\oplus z\\geq x\\oplus y\\)\\(x\\oplus z \\geq y\\oplus z\\)。定义 \\(p(x,y)\\) 为从高位向低位看,\\(x\\)\\(y\\) 第一个不同的位。我们考虑这样一个过程,将 \\(x\\) 一点一点的向上增加,那么一定是 \\(x\\) 的低位先变动、\\(x\\) 的高位后变动。在这个过程中,\\(x\\) 先达到 \\(y\\),再达到 \\(z\\)。所以一定有 \\(p(x,z)\\geq p(x,y)\\)。当然,这只是一个很不严谨的、非常感性的理解。具体证明看下面:

最后,设 DP 状态为 \\(f[i]\\) 表示强制以 \\(i\\) 结尾的合法子序列的数目。则有 DP 转移方程

\\[f[i]=1+\\sum\\limits_{\\{j|a_i\\oplus a_j\\geq x\\}}f[j] \\]

时间复杂度:\\(O(n^2)\\)

如何优化呢?看到亦或,当然要想到trie树。每次只需在trie树找出与 \\(x\\) 亦或起来大于等于 \\(x\\) 的所有数对应的DP值之和即可。

时间复杂度:\\(O(n\\log {maxa})\\)

三、代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define FILEIN(s) freopen(s".in", "r", stdin);
#define FILEOUT(s) freopen(s".out", "w", stdout)
#define mem(s, v) memset(s, v, sizeof s)

inline long long read(void) {
    long long x = 0, f = 1; char ch = getchar();
    while (ch < \'0\' || ch > \'9\') { if (ch == \'-\') f = -1; ch = getchar(); }
    while (ch >= \'0\' && ch <= \'9\') { x = x * 10 + ch - \'0\'; ch = getchar(); }
    return f * x;
}

const int maxn = 3e5 + 5, mod = 998244353;

int n, sz = 1;
long long X, a[maxn], f[maxn];

int trans[maxn * 80][2];
long long sum[maxn * 80];

inline int call(long long x, int i) { return (x >> i) & 1; }

inline void insert(long long x, long long val) {
    int p = 1;
    for (int i = 59; i >= 0; -- i) {
        int now = call(x, i);
        if (!trans[p][now]) trans[p][now] = ++ sz;
        p = trans[p][now];
        (sum[p] += val) %= mod;
    }
}

inline long long query(long long x) {
    int p = 1;
    long long res = 0;
    for (int i = 59; i >= 0; -- i) {
        int now1 = call(x, i), now2 = call(X, i);
        if (now2 == 0) {
            if (trans[p][now1 ^ 1]) res += sum[trans[p][now1 ^ 1]];
        } 
        if (!trans[p][now1 ^ now2]) trans[p][now1 ^ now2] = ++ sz;
        p = trans[p][now1 ^ now2];
    }
    (res += sum[p]) %= mod;
    return res;
}

int main() {
    FILEIN("xor"); FILEOUT("xor");
    n = read(); X = read();
    for (int i = 1; i <= n; ++ i)
        a[i] = read();
    sort(a + 1, a + n + 1); 
    f[1] = 1;
    insert(a[1], f[1]);
    for (int i = 2; i <= n; ++ i) {
        int tmp = query(a[i]);
        f[i] = 1 + tmp;
        insert(a[i], f[i]);
    }
    long long res = 0;
    for (int i = 1; i <= n; ++ i) (res += f[i]) %= mod;
    printf("%lld\\n", res);
    return 0;
}

以上是关于疑惑 题解的主要内容,如果未能解决你的问题,请参考以下文章

一道数学恶心题——小凯的疑惑

c++ 中有关键字 signals ?(阅读eva源码时的疑惑)

Dove 的疑惑

P1906 凯撒密码 题解

Java 求解划分字母区间

noip做题记录+挑战一句话题解?