当前位置: 首页 > news >正文

Codeforces Round 1016 (Div. 3)题解

题目地址

https://codeforces.com/contest/2093

锐评

在所有题意都理解正确的情况下,整体难度不算太难。但是偏偏存在F这么恶心的题意,样例都不带解释一下的,根本看不懂题。D题也恶心,在于递归过程的拆分,需要点数学,跟打印递归定义的图形一样,写麻了,好在过了。E题居然卡双 l o g log log 做法常数,也是恶心。反而是G题很典,太裸了,可惜被D防住了,根本没看到G题。再次陷入“看完所有题不会写,不看完所有题却会写”的魔咒。主要还是自己太菜了,打破不了这个魔咒,大佬们就没这个烦恼。

题解

Problem A. Ideal Generator

题目大意

k k k 个正整数组成的数组 a a a [ a 1 , a 2 , … , a k ] = [ a k , a k − 1 , … , a 1 ] [a_1, a_2, \dots, a_k] = [a_k, a_{k-1}, \dots, a_1] [a1,a2,,ak]=[ak,ak1,,a1] 的情况下称为回文数组(其实就是正着读反着读是一样的)。例如,数组 [ 1 , 2 , 1 ] [1, 2, 1] [1,2,1] [ 5 , 1 , 1 , 5 ] [5, 1, 1, 5] [5,1,1,5] 是回文数组,而数组 [ 1 , 2 , 3 ] [1, 2, 3] [1,2,3] [ 21 , 12 ] [21, 12] [21,12] 不是回文数组。

如果任何整数 n n n ( n ≥ k n \geq k nk ) 都可以表示为一个长度正好为 k k k 的回文数组的元素之和,我们就称这个数 k k k 为理想生成数。数组中的每个元素都必须大于 0 0 0

例如,数字 1 1 1 是一个理想生成数,因为任何自然数 n n n 都可以用数组 [ n ] [n] [n] 生成。然而,数字 2 2 2 并不是一个理想生成数,因为不存在长度为 2 2 2 的和为 3 3 3 的回文数组。

请判断给定的数字 k k k 是否是理想生成数。

题解思路:思维

先通过样例观察,发现奇数可以,偶数不行。开始验证,假如和为 k k k ,那么全部数组元素为 1 1 1 即可,假如和为 k + 1 k + 1 k+1 ,那么全部数组元素为 1 1 1 的基础上,有一个数要加上 1 1 1 还要是回文数组,那么只能放在最中间的位置上了,不然所放位置对称的那一个位置就不相等了。又因为 n n n 是连续的,所以差值为 1 1 1 只有数组长度是奇数才能满足,每次都在最中间位置加上 1 1 1 。时间复杂度为 O ( 1 ) O(1) O(1)

参考代码(C++)

#include <bits/stdc++.h>
using namespace std;
int n;

void solve() {
    cin >> n;
    cout << ((n & 1) ? "YES\n" : "NO\n");
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

Problem B. Expensive Number

题目大意

正整数 n n n 的代价被定义为数字 n n n 除以其数位之和的结果。

例如,数字 104 104 104 的代价是 104 1 + 0 + 4 = 20.8 \frac{104}{1 + 0 + 4} = 20.8 1+0+4104=20.8 ,数字 111 111 111 的代价是 111 1 + 1 + 1 = 37 \frac{111}{1 + 1 + 1} = 37 1+1+1111=37

给你一个不包含前导零的正整数 n n n 。你可以从数字 n n n 中删除任意数位(包括不删除),这样剩下的数字至少包含一位数,并且严格大于零。剩下的数字不能重新排列。因此,你可能得到一个前导为零的数字。

例如,给你一个数字 103554 103554 103554 。如果去掉 1 1 1 4 4 4 和一个数字 5 5 5 ,最后得到的数字是 035 035 035 ,其代价是 035 0 + 3 + 5 = 4.375 \frac{035}{0 + 3 + 5} = 4.375 0+3+5035=4.375

为了使代价最小,你需要从这个数字中删除最少多少个数字?

题解思路:贪心

首先,一个数字的数位之和是不可能大于这个数字的,最多和它相等。那么代价最小意味着什么?显然就是相等。所以只有一位数字时代价达到最小,代价为 1 1 1 。因为题目删除数位后允许有前导 0 0 0 ,所以选定某个数字前面的 0 0 0 可以不删除。又因为题目要求删除后组成的这个数必须严格大于 0 0 0 ,所以我们要找一个非 0 0 0 数位。因为前导 0 0 0 可以保留,后导 0 0 0 不能保留(保留就不是个位数了),所以我们倒着枚举,找到第一个非 0 0 0 数位位置,将这个位置前面的非 0 0 0 数位删除以及后面的数位删除,删除的数位个数即是答案。时间复杂度为 O ( n ) O(n) O(n)

参考代码(C++)

#include <bits/stdc++.h>
using namespace std;
string str;

void solve() {
    cin >> str;
    int n = str.size();
    int id = n - 1;
    for (int i = n - 1; i >= 0; --i)
        if (str[i] != '0') {
            id = i;
            break;
        }
    int ans = n - 1 - id;
    for (int i = id - 1; i >= 0; --i)
        if (str[i] != '0')
            ++ans;
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

Problem C. Simple Repetition

题目大意

帕夏喜欢质数!为了找到生成质数的新方法,他再次对互联网上的一种算法产生了兴趣:

  • 要得到一个新数字 y y y ,重复 k k k 次数字 x x x 的十进制表示 x x x (不含前导零)。

例如, x = 52 x = 52 x=52 k = 3 k = 3 k=3 可以得到 y = 525252 y = 525252 y=525252 x = 6 x = 6 x=6 k = 7 k = 7 k=7 可以得到 y = 6666666 y = 6666666 y=6666666

帕夏非常希望得到的数字 y y y 是质数,但他还不知道如何检验这种算法生成的数字的质性。请帮助帕夏,告诉他 y y y 是否是质数!

如果一个整数 x 只有 2 个不同的除数 1 和 x ,那么这个整数 x 就是质数。例如, 13 是质数,因为它只有 2 个除数: 1 和 13 。请注意,数字 1 不是质数,因为它只有一个除数。

题解思路:思维/分类讨论

我们来一一分析下。

  • k = 1 k = 1 k=1 ,显然只需要判定 x x x 是否质数。
  • k > 1 k \gt 1 k>1 ,即 x x x 至少重复了 2 2 2 次,设 x x x n n n 个数位,那么 y y y 显然有一个除数 x x x ,使得 y x = a 1 0 ⋯ 0 ⏟ n − 1 个 a 2 0 ⋯ 0 ⏟ n − 1 个 … a k \frac{y}{x} = a_1 \underbrace{0 \cdots 0}_{n - 1个} a_2 \underbrace{0 \cdots 0}_{n - 1个} \dots a_k xy=a1n1 00a2n1 00ak ,其中 a i = 1 , 1 ≤ i ≤ k a_i = 1, 1 \leq i \leq k ai=1,1ik 。那么只要 1 < x < y 1 \lt x \lt y 1<x<y y y y 必然不是质数,显然 x < y x \lt y x<y 必然成立,所以只需要再单独判断一下 x x x 1 1 1 的情况即可。

根据上面的分析,问题得解。时间复杂度为 O ( 1 ) O(1) O(1)

参考代码(C++)

#include <bits/stdc++.h>
using namespace std;
int n, m;

bool check(int x) {
    if (x < 2)
        return false;
    for (int i = 2; i * i <= x; ++i)
        if (x % i == 0)
            return false;
    return true;
}

void solve() {
    cin >> n >> m;
    if (m == 1)
        cout << (check(n) ? "YES\n" : "NO\n");
    else if (n == 1) {
        int x = 0;
        for (int i = 0; i < m; ++i)
            x = x * 10 + 1;
        cout << (check(x) ? "YES\n" : "NO\n");
    } else
        cout << "NO\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

Problem D. Skibidi Table

题目大意

瓦迪姆喜欢用整数填充方形表格。不过今天他想到了一个好玩的方法!以大小为 2 × 2 2 \times 2 2×2 的表格为例,表格的行从上到下编号,列从左到右编号。我们将 1 1 1 置于左上角单元格, 2 2 2 置于右下角单元格, 3 3 3 置于左下角单元格, 4 4 4 置于右上角单元格。这就是他所需要的全部乐趣!

幸运的是,瓦迪姆有一个大小为 2 n × 2 n 2^n \times 2^n 2n×2n 的表格。他计划用从 1 1 1 2 2 n 2^{2n} 22n 的整数按升序填满它。为了填满这样一个大表,瓦迪姆将把它分成 4 4 4 个相等的方表,先填满左上角的表,然后填满右下角的表,接着填满左下角的表,最后填满右上角的表。在填满每张小方表的过程中,他又会把每张小方表分割成更小的表,直到填满 2 × 2 2 \times 2 2×2 大小的方表为止。

现在瓦迪姆迫不及待地想开始填表,但是他有两类 q q q 个问题:

  • x x x 行第 y y y 列的单元格中的数字是多少
  • 数字 d d d 位于哪个单元格坐标

帮助回答瓦迪姆的问题。

题解思路:DFS

题意倒是很直接,思路也很明确,就是不断DFS缩小区域。但是这个区域怎么设计还真是恶心,会的很会,不会的真的会卡很久,看群友有被卡两小时的。

首先对于块的大小,假如当前处于第 n n n 层,块的大小为 2 n − 1 × 2 n − 1 2^{n - 1} \times 2^{n - 1} 2n1×2n1 ,即是宽高各减一半。其次是对于坐标步长,根据前面分析(宽高各减一半),可知步长就是 2 n − 1 2^{n - 1} 2n1 。知道这两个性质就好办了,只需要知道当前处于第几层,以及当前层的左上角坐标,即可一步步缩小范围,直到不能再缩小,即是答案,详见代码。时间复杂度为 O ( n q ) O(nq) O(nq)

参考代码(C++)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
int n, q;

ll dfs1(int cur, int l, int r, int x, int y) {
    // cout << "dfs1:" << cur << ':' << l << ':' << r << ':' << x << ':' << y << '\n';
    if (l == x && r == y)
        return 1;
    ll dt = 1LL << (cur - 1);
    ll dd = dt * dt;
    if (x >= l + dt && y >= r + dt)
        return dd + dfs1(cur - 1, l + dt, r + dt, x, y);
    if (x >= l + dt)
        return (dd << 1) + dfs1(cur - 1, l + dt, r, x, y);
    if (y >= r + dt)
        return 3 * dd + dfs1(cur - 1, l, r + dt, x, y);
    return dfs1(cur - 1, l, r, x, y);
}

pii dfs2(int cur, int l, int r, ll d) {
    // cout << "dfs2:" << cur << ':' << l << ':' << r << ':' << d << '\n';
    if (d == 1)
        return {l, r};
    ll dt = 1LL << (cur - 1);
    ll dd = dt * dt;
    if (d > 3 * dd)
        return dfs2(cur - 1, l, r + dt, d - 3 * dd);
    if (d > (dd << 1))
        return dfs2(cur - 1, l + dt, r, d - (dd << 1));
    if (d > dd)
        return dfs2(cur - 1, l + dt, r + dt, d - dd);
    return dfs2(cur - 1, l, r, d);
}

void solve() {
    cin >> n >> q;
    string op;
    int x, y;
    ll d;
    while (q--) {
        cin >> op;
        if (op == "->") {
            cin >> x >> y;
            cout << dfs1(n, 1, 1, x, y) << '\n';
        } else {
            cin >> d;
            pii ans = dfs2(n, 1, 1, d);
            cout << ans.first << ' ' << ans.second << '\n';
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

Problem E. Min Max MEX

题目大意

给你一个长度为 n n n 的数组 a a a 和一个数字 k k k

子数组的定义是数组中一个或多个连续元素的序列。你需要将数组 a a a 分割成 k k k 个不重叠的子数组 b 1 , b 2 , … , b k b_1, b_2, \dots, b_k b1,b2,,bk ,使得这些子数组的合集等于整个数组。此外,你需要最大化 x x x 的值,即 x = min ⁡ ( M E X ( b i ) ) , 1 ≤ i ≤ k x = \min(MEX(b_i)), 1 \leq i \leq k x=min(MEX(bi)),1ik

M E X ( v ) MEX(v) MEX(v) 表示数组 v v v 中没有的最小非负整数。

题解思路:二分

对于 u = M E X ( v ) u = MEX(v) u=MEX(v) ,如果选择数组 v v v 的一部分数组成数组 v t vt vt ,那么对于所有 w < u w \lt u w<u ,是否都能找到 w = M E X ( v t ) w = MEX(vt) w=MEX(vt) ?答案是肯定的。所以我们考虑二分,下限 l = 0 l = 0 l=0 ,上限 r = n r = n r=n (因为数组顶多是 [ 0 , 1 , … , n − 1 ] [0, 1, \dots, n - 1] [0,1,,n1] )。那么我们怎么去check呢?对于 M E X MEX MEX u u u ,我们只需要维护一个集合,然后遍历整个数组,对于每个元素,满足 a i < u , 0 ≤ i < n a_i \lt u, 0 \leq i \lt n ai<u,0i<n ,就将其加入集合,当集合元素个数达到了 u u u ,然后计数加一(表示可以划分为一个子数组,满足 M E X ≥ u MEX \geq u MEXu),并且清空当前集合。这样到最后,只要计数大于等于 k k k ,表示可以合理划分。时间复杂度为 O ( n l o g n l o g n ) O(nlognlogn) O(nlognlogn) (check用到了set,换成数组每次标记取反可以降到 O ( n l o g n ) O(nlogn) O(nlogn) )。

PS:此题居然卡双 l o g log log 做法常数,真是无语啊!

参考代码(C++)

l o g log log 超时代码。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int maxn = 200'005;
int a[maxn];
int n, m;

bool check(int x) {
    set<int> st;
    for (int i = 0; i < x; ++i)
        st.insert(i);
    if (st.empty())
        return true;
    set<int> stc;
    int cnt = 0;
    for (int i = 0; i < n; ++i) {
        if (a[i] < x)
            stc.insert(a[i]);
        if (stc.size() == st.size()) {
            ++cnt;
            stc.clear();
            if (cnt >= m)
                return true;
        }
    }
    return cnt >= m;
}

void solve() {
    cin >> n >> m;
    for (int i = 0; i < n; ++i)
        cin >> a[i];
    int l = 0, r = n + 1, ans = -1;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(mid)) {
            ans = mid;
            l = mid + 1;
        } else
            r = mid - 1;
    }
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

l o g log log 通过代码。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int maxn = 200'005;
int a[maxn];
int n, m;

bool check(int x) {
    set<int> st;
    int cnt = 0;
    for (int i = 0; i < n; ++i) {
        if (a[i] < x)
            st.insert(a[i]);
        if (st.size() == x) {
            ++cnt;
            st.clear();
            if (cnt >= m)
                return true;
        }
    }
    return cnt >= m;
}

void solve() {
    cin >> n >> m;
    for (int i = 0; i < n; ++i)
        cin >> a[i];
    int l = 1, r = n, ans = 0;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(mid)) {
            ans = mid;
            l = mid + 1;
        } else
            r = mid - 1;
    }
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

l o g log log 通过代码。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int maxn = 200'005;
int a[maxn];
bool vis[maxn];
int n, m;

bool check(int x) {
    for (int i = 0; i < x; ++i)
        vis[i] = false;
    bool f = true;
    int cnt = 0, cur = 0;
    for (int i = 0; i < n; ++i) {
        if (a[i] < x) {
            if (vis[a[i]] != f) {
                ++cur;
                vis[a[i]] = f;
            }
        }
        if (cur == x) {
            ++cnt;
            cur = 0;
            f = !f;
            if (cnt >= m)
                return true;
        }
    }
    return cnt >= m;
}

void solve() {
    cin >> n >> m;
    for (int i = 0; i < n; ++i)
        cin >> a[i];
    int l = 1, r = n, ans = 0;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(mid)) {
            ans = mid;
            l = mid + 1;
        } else
            r = mid - 1;
    }
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

Problem F. Hackers and Neural Networks

题目大意

黑客们再次试图利用神经网络的输出创建娱乐短语。这一次,他们想获得长度为 n n n 的字符串数组 a a a

最初,他们有一个长度为 n n n 的数组 c c c ,其中充满了空白,用符号 ∗ * 表示。因此,如果 n = 4 n = 4 n=4 ,则初始值为 c = [ ∗ , ∗ , ∗ , ∗ ] c=[*, *, *, *] c=[,,,]

黑客可以访问 m m m 个神经网络,每个神经网络都有自己的请求答案版本–长度为 n n n 的字符串数组 b i b_i bi

黑客试图通过以下操作从数组 c c c 中获取数组 a a a

  • 选择神经网络 i i i ,对数组 c c c 执行下一步操作:选择一个随机空白,例如在位置 j j j 处,将 c j c_j cj 替换为 b i , j b_{i, j} bi,j

    例如,如果选择了第一个神经网络 b 1 = [ «I» , «love» , «apples» ] b_1 = [\text{«I»}, \text{«love»}, \text{«apples»}] b1=[«I»,«love»,«apples»] ,当前 c = [ ∗ , «like» , ∗ ] c = [*, \text{«like»}, *] c=[,«like»,] ,那么在对第一个神经网络进行操作后, c c c 可能会变成 [ «I» , «like» , ∗ ] [\text{«I»}, \text{«like»}, *] [«I»,«like»,] [ ∗ , «like» , «apples» ] [*, \text{«like»}, \text{«apples»}] [,«like»,«apples»]

  • 选择位置 j j j 并将 c j c_j cj 替换为空白。

不幸的是,由于黑客访问神经网络的方式,他们只能在所有操作完成后才能看到修改后的数组 c c c ,因此他们必须事先指定整个操作序列。

然而,神经网络的随机行为可能会导致永远无法获得所需的数组,或者获得所需的数组需要过多的操作。

因此,黑客们希望您能帮助他们选择一个操作序列,以保证在最少的操作次数内获得数组 a a a

更具体地说,如果存在一个操作序列可以保证从数组 c c c 中获得数组 a a a ,那么在所有这样的序列中,找出一个操作次数最少的序列,并输出其中的操作次数。

如果没有将数组 c c c 转换成数组 a a a 的操作序列,则输出 − 1 -1 1

题解思路:贪心

题意真的很长且很拉,真的看完好像不知道要求什么?让我们再细细品味一下!反正就是进行两个操作嘛,只要对应位置的字符串不对就一定要继续操作。只要操作,那么操作次数必然会增加。

假如某个操作后,某个位置已经是正确的,下一次操作你会不会去改它?显然不会了,不然你还得再至少进行一次操作二以及至少随机一次操作一,而且随机后不一定是对的,何必呢?

如果所有位置都是空的,你会不会进行操作二?显然也不会,白白浪费一次操作嘛。所以第一次操作肯定是操作一,这是个随机过程。

通过上面的分析,我们唯一能决定的就是可以选择跑哪个神经网络。从概率论角度来说,我们当然希望选择命中概率更高的,这样所得的期望就越大,后续所需要的操作就更少。所以第一次操作就至关重要了,我们就选命中概率最大的神经网络,这样我们就能保证 n n n 次操作后,随机正确位置最大。这样所有位置都被填满了,最后对不正确的位置,我们只需要先执行一次操作二,再找到一个神经网络,其对应位置存在正确字符串,因为只会空白位置随机,而当前空白位置只有一个,显然这是一个必然事件。

上面操作一定是最优的吗?一定的。假设你选择某个神经网络的命中率是 x y \frac{x}{y} yx ,你把其他所有的神经网络全部组合起来,命中率形如 x + a y + b \frac{x + a}{y + b} y+bx+a ,其不可能更大。

对于不存在的情况,显然所有对应位置都没有目标串,就无法做到。时间复杂度为 O ( m n max ⁡ ( ∣ b i , j ∣ ) ) O(mn \max(|b_{i, j}|)) O(mnmax(bi,j))

参考代码(C++)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int maxn = 505;
string p[maxn], str[maxn][maxn];
int cntr[maxn], cntc[maxn];
int n, m;

void solve() {
    cin >> n >> m;
    for (int i = 0; i < n; ++i) {
        cin >> p[i];
        cntc[i] = 0;
    }
    for (int i = 0; i < m; ++i) {
        cntr[i] = 0;
        for  (int j = 0; j < n; ++j) {
            cin >> str[i][j];
            if (str[i][j] == p[j]) {
                ++cntc[j];
                ++cntr[i];
            }
        }
    }
    for (int i = 0; i < n; ++i)
        if (cntc[i] == 0) {
            cout << "-1\n";
            return;
        }
    int maxc = 0;
    for (int i = 0; i < m; ++i)
        maxc = max(maxc, cntr[i]);
    cout << (n + ((n - maxc) << 1)) << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

Problem G. Shorten the Array

题目大意

长度为 m m m 的数组 b b b 的美感定义为所有可能数对 1 ≤ i ≤ j ≤ m 1 \leq i \leq j \leq m 1ijm 中的 max ⁡ ( b i ⊕ b j ) \max(b_i \oplus b_j) max(bibj) ,其中 x ⊕ y x \oplus y xy 是数字 x x x y y y 的 bitwise XOR。我们将数组 b b b 的美感表示为 f ( b ) f(b) f(b)

如果数组 b b b 中有 f ( b ) ≥ k f(b) \geq k f(b)k ,那么这个数组 b b b 就叫做美丽数组。

最近,科斯佳从商店买了一个长度为 n n n 的数组 a a a 。他认为这个数组太长了,所以打算从中剪切出一些美丽的子数组。也就是说,他想选择数字 l l l r r r ( 1 ≤ l ≤ r ≤ n 1 \leq l \leq r \leq n 1lrn ),这样数组 a l … r a_{l \dots r} alr 就很美丽了。这样一个子数组的长度为 r − l + 1 r - l + 1 rl+1 。整个数组 a a a 也被视为一个子数组(包含 l = 1 l = 1 l=1 r = n r = n r=n )。

你的任务是找出数组 a a a 中最短的美丽子数组的长度。如果没有一个子数组是美丽的,那么你应该输出数字 − 1 -1 1

题解思路:双指针+字典树Trie

首先,对于每个 l l l ,如果找到第一个满足条件的 r ( r ≥ l ) r(r \geq l) r(rl) ,那么显然 r + 1 ( r < n ) r + 1(r \lt n) r+1(r<n) 也可以。既然这样,那么我们维护一个双指针,对于每个左指针,不断扩展右指针,直到找到第一个满足条件的位置,更新答案即可。那么怎么快速计算出当前区间是否可以满足条件呢?很容易就会想到字典树求当前区间可以得到的最大异或值。时间复杂度为 O ( n ) O(n) O(n) (计算次数实际为 30 n 30n 30n ,常数忽略,但实际运行时间还是要考虑的)。

参考代码(C++)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int maxn = 200'005;
const int maxnode = 6'000'005;
const int sigma_size = 2;
struct trie {
    int child[maxnode][sigma_size];
    int value[maxnode];
    int size;

    void init() {
        size = 1;
        memset(child[0], 0, sizeof(child[0]));
    }

    void insert(int x, int y) {
        int pos = 0;
        for (int i = 29; i >= 0; --i) {
            int id = (x >> i) & 1;
            if (!child[pos][id]) {
                memset(child[size], 0, sizeof(child[size]));
                value[size] = 0;
                child[pos][id] = size++;
            }
            pos = child[pos][id];
            value[pos] += y;
        }
    }

    int query(int x) {
        // cout << "query: " << x << '\n';
        int pos = 0, ans = 0;
        for (int i = 29; i >= 0; --i) {
            int id = (x >> i) & 1;
            int idx = id ^ 1;
            int p = child[pos][idx];
            if (p && value[p]) {
                ans |= 1 << i;
                pos = p;
            } else {
                p = child[pos][id];
                if (p && value[p])
                    pos = p;
                else
                    return -1;
            }
        }
        // cout << "query: ans = " << ans << '\n';
        return ans;
    }
} tr;
int a[maxn];
int n, m;

void solve() {
    cin >> n >> m;
    for (int i = 0; i < n; ++i)
        cin >> a[i];
    if (m == 0) {
        cout << "1\n";
        return;
    }
    tr.init();
    int l = 0, r = 0, ans = n + 1;
    while (r < n) {
        // cout << l << ", " << r << endl;
        while (r < n && tr.query(a[r]) < m)
            tr.insert(a[r++], 1);
        if (r < n)
            ans = min(ans, r - l + 1);
        tr.insert(a[l++], -1);
        if (l > r)
            r = l;
    }
    cout << (ans == n + 1 ? -1 : ans) << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

相关文章:

  • Django软删除功能完整指南:构建图书馆项目
  • 【Docker项目实战】使用Docker部署ToDoList任务管理工具
  • 亮相CMEF,美的医疗全维度打造智慧医疗新生态
  • HTML的Canvas元素
  • 数字的乘阶运算
  • 零碳园区智慧能源解决方案
  • 【算法学习】链表篇:链表的常用技巧和操作总结
  • SvelteKit 最新中文文档教程(19)—— 最佳实践之身份认证
  • 控制 ElementUI el-table 树形表格多选框的显示层级
  • NLP高频面试题(三十七)——大模型训练和推理的显存估计
  • 深入解析原生鸿蒙中的 RN 日志系统:从入门到精通!
  • Go 语言中的select是做什么的
  • 什么是音频预加重与去加重,预加重与去加重的原理是什么,在什么条件下会使用预加重与去加重?
  • 今日踩坑之@Autowired与@Resource区别
  • 解锁ChatGPT-4o文生图潜力:精选提示词收集整理更新中
  • vLLM实战:单机多卡大模型推理部署指南
  • 天元证券|空仓一个月 这批新基金冲进去了!
  • Cadence学习笔记之---热风焊盘制作
  • 从ETL到ELT:大数据时代下两者的选型建议及优势
  • 指纹浏览器技术架构解析:高并发批量注册业务的工程化实践——基于分布式指纹引擎与防关联策略的深度实现
  • 重新设置wordpress/谷歌网站优化推广
  • 程序源代码网站/网站seo推广优化
  • 营销型网站服务/友情链接多久有效果
  • 关于英文网站建设的请示/如何做好网站站内优化
  • 深圳市宝安区建设局网站/做营销怎样才能吸引客户
  • 网站海外推广平台/搜索引擎优化指的是