牛客刷题-Day21

牛客刷题-Day21

今日刷题:\(1031-1035\)

1031 贪心 · 例10-Bits

6ed536a2-e2e0-42f2-8615-35a13ac853b6

解题思路

贪心。假设开始每一位都是 \(1\),从高位 \(i\) 开始枚举,如果当前数 \(>r\),且减去 \(1<<i\) 后仍 \(>=l\),就减 \(1<<i\)。当当前数在 \([l,r]\) 之间时,输出。
因为从高位开始减,所以保证当前数是最小的。
参考:Codeforces Round #276 (Div. 1) A. Bits

C++ 代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 70;

int T;
LL bit[N];

int main() {
    scanf("%d", &T);
    bit[0] = 1;
    for (int i = 1; i <= 60; i++)
        bit[i] = bit[i - 1] * 2;
    while (T--) {
        LL l, r;
        scanf("%lld%lld", &l, &r);
        LL res = ((LL) 1 << 61) - 1;
        for (int i = 60; i >= 0; i--) {
            if (res > r && res - bit[i] >= l)
                res -= bit[i];
            if (res >= l && res <= r)
                break;
        }
        printf("%lld\n", res);
    }
    return 0;
}

1032 贪心 · 例11-毒瘤xor

6d9c46b1-b2cb-4849-b903-77d008a9548f

解题思路

异或运算每一位是独立的,因此,对于每一位,统计该区间内所有数在该位的 \(0\)\(1\) 的个数。
因为要在异或操作之后保留更多的 \(1\),因此对于 \(x\) 的对应位,如果 \(1\) 多则取 \(0\),反之取 \(1\)

C++ 代码

#include <bits/stdc++.h>
using namespace std;
const int N = 100010, M = 35;
typedef long long LL;

int n, q;
int a[N], s[N][M]; // s[i][j] 表示前 i 个数第 j 位 1 的个数

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= 30; j++)
            s[i][j] = s[i - 1][j] + ((a[i] >> j) & 1);
    }
    scanf("%d", &q);
    while (q--) {
        int l, r, res = 0;
        scanf("%d%d", &l, &r);
        for (int j = 0; j <= 30; j++) {
            if (2 * (s[r][j] - s[l - 1][j]) <= r - l + 1)
                res |= 1 << j;
        }
        printf("%d\n", res);
    }
    return 0;
}

1033 贪心 · 例12-兔子的区间密码

fce07855-2288-4f2b-a7a1-f30ffd49d448

解题思路

对于区间端点 \(l\)\(r\),按位来遍历,如果高位都相同,那么高位是无法改变的,继续遍历,只要某位开始不同,那么异或的结果就是这一位往低位都可以变为全 \(1\)
假设前 \(k\) 位相同,第 \(k+1\) 位不同。

注意:第 \(k+1\) 位不同,则 \(l\)\(k+1\) 位必然为 \(0\)\(r\)\(k+1\) 位必然为 \(1\)。如果反之,则 \(l\)\(xxx1aaa\)\(r\)\(xxx0bbb\),很明显 \(l>r\)

\(l\) 的从 \(k+2\) 位到最后都可以取到 \(1\),这样得到的数必然大于等于 \(l\)\(r\) 的从 \(k+2\) 位到最后都可以取到 \(0\),这样得到的数必然小于等于 \(r\)。这样得到的两个数存在 \(k+1\) 到最低位都不同,异或之后值最大。

C++ 代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

int T;

void solve(LL l, LL r) {
    for (int i = 63; i >= 0; i--) {
        if ((l >> i) == (r >> i))
            continue;
        printf("%lld\n", ((LL) 1 << (i + 1)) - 1);
        return;
    }
    printf("0\n");
}

int main() {
    scanf("%d", &T);
    while (T--) {
        LL l, r;
        scanf("%lld%lld", &l, &r);
        solve(l ,r);
    }
    return 0;
}

1034 贪心 · 例13-起床困难综合征

507ce874-05fe-4e93-8681-d0fc1a77d511

解题思路

\(a=0\)\(b=-1\) 进行一边操作,这样就可以得到每一位在初始为 \(0\) 或者 \(1\) 经过操作得到的结果。
因为初始攻击力要在 \([0,m]\) 之间,因此每一位可以从 \(0\) 经过操作得到 \(1\),则该位优先取 \(0\);否则取 \(1\),并计算剩余可用值(范围限制)。
因为要保证答案最大,因此从高位开始遍历。

for (int j = 29; j >= 0; j--) {
	if ((a >> j) & 1)
		res += (1 << j);
	else if ((b >> j) & 1 && ((1 << j) <= m))
		res += (1 << j), m -= (1 << j);
}

C++ 代码

#include <bits/stdc++.h>
using namespace std;
const int N = 200010;

int n, m, t;
string op;

int main() {
    int a = 0, b = -1;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) { // 每一位为 0 或者 1 进行操作
        cin >> op >> t;
        if (op == "OR") {
            a |= t, b |= t;
        } else if (op == "XOR") {
            a ^= t, b ^= t;
        } else {
            a &= t, b &= t;
        }
    }
    int res = 0;
    for (int j = 29; j >= 0; j--) {
        if ((a >> j) & 1)
            res += (1 << j);
        else if ((b >> j) & 1 && ((1 << j) <= m))
            res += (1 << j), m -= (1 << j);
    }
    cout << res << endl;
    return 0;
}

1035 习题-[NOIP2017]时间复杂度

解题思路

首先判断循环是否可执行:

  • 如果 \(x=n\)\(y=n\),为 \(O(1)\)\(y\) 为常数,无法执行;
  • 如果 \(y=n\)\(x\) 为常数,为 \(O(n^1)\)
  • 如果 \(x\)\(y\) 都为常数,且 \(x\le y\),为 \(O(1)\)

当输入为循环开始语句,判断变量是否已声明,已声明为 \(ERR\),否则(当前循环判断值为 \(t\)):

  • 栈为空:当前不存在嵌套循环,上述判断的值直接入栈,可更新答案 \(res=max(res,t)\)
  • 栈不空:当前存在嵌套循环,判断外层循环是否可执行,若外层不可执行或者当前循环不可执行,则 \(-1\) 入栈;否则 \(top+t\) 入栈,可更新答案 \(res=max(res,t+top)\)

当输入为循环结束语句:

  • 栈为空:不合法。
  • 栈不空:栈顶弹出,变量弹出,一个循环结束。

C++ 代码

#include <bits/stdc++.h>
using namespace std;
const int N = 110;

int T, L;
string complexity, loop[N];

int get(string x, string y) {
    if (x == "n" && y == "n") return 0;
    else if (x == "n") return -1;
    else if (y == "n") return 1;
    else if (stoi(x) <= stoi(y)) return 0;
    else return -1;
}

string calc(int L) {
    int res = 0;
    stack<int> stk;
    string vars;
    for (int i = 0; i < L; i++) {
        auto &s = loop[i];
        if (s[0] == 'F') {
            char var[2], x[4], y[4];
            sscanf(s.c_str(), "F %s %s %s", var, x, y);
            if (vars.find(var) != -1)
                return "ERR";
            vars += var;
            int t = get(x, y);
            if (stk.empty()) {
                stk.push(t);
                res = max(res, t);
            } else {
                int top = stk.top();
                if (top == -1 || t == -1)
                    stk.push(-1);
                else {
                    stk.push(top + t);
                    res = max(res, top + t);
                }
            }
        } else {
            if (stk.empty())
                return "ERR";
            stk.pop();
            vars.pop_back();
        }
    }
    if (stk.size()) return "ERR";
    if (!res) return "O(1)";
    return "O(n^" + to_string(res) + ")";
}

int main() {
    cin >> T;
    while (T--) {
        cin >> L >> complexity;
        getchar();
        for (int i = 0; i < L; i++)
            getline(cin, loop[i]);
        string res = calc(L);
        if (res == "ERR")
            cout << res << endl;
        else if (res == complexity)
            cout << "Yes" << endl;
        else
            cout << "No" << endl;
    }
    return 0;
}
posted @ 2025-11-11 16:59  Cocoicobird  阅读(0)  评论(0)    收藏  举报