Codeforces Round #769 (Div. 2)

A. ABC
题目简述:

​ 给定01字符串S ,判断其是否有长度 >= 2 的回文子串,有则输出YES,无则输出NO。

思路:

​ 对于长度 >= 3 的 S 必定包含长度 >= 2的回文子串,当长度为2或1时注意特判。

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

void solve()
{
    int n;
    string s;

    cin >> n >> s;

    if (n == 1) {
        cout << "YES" << endl;
    } else if (n == 2) {
        if (s[0] == s[1])
            cout << "NO" << endl;
        else 
            cout << "YES" << endl;
    } else {
        cout << "NO" << endl;
    }
}

int main()
{
    ios::sync_with_stdio(0);
    int t;
    cin >> t;

    while (t --)
        solve();

    return 0;
}
B. Roof Construction
题目简述:

​ 给定长度为 n 的排列数a, 权值从 0 到 n - 1, 设该排列数的总花费为 m a x 1 < = i < = n − 1   p i ⨁ p i + 1 max_{1<=i<=n-1} \ p_i \bigoplus p_{i+1} max1<=i<=n1 pipi+1 , 求构造一种排列序列使总花费最小。

思路:

​ 显然构造成本为相邻异或的最大值,该最大值由二进制最高位决定,由 n 降序输出直至 log(n), 在从 0 升序输出到 log(n)

代码:
#include <bits/stdc++.h>

using namespace std;

void solve()
{
    int a, b;
    cin >> a >> b;

    // 初始答案设置为 b - a
    int ans = b - a;
    // 枚举 a1
    for (int a1 = a; a1 < b; a1 ++) {
        int b1 = 0;
        // 确定b1
        for (int i = 21; i >= 0; i --) {
            if ((b >> i) & 1) {
                // b1 二进制高位与 b 相同
                b1 ^= (1 << i);
            } else if ((a1 >> i) & 1){
                // b1 当前仅当a1该二进制位为0,b为1时更改,同时结束循环。
                b1 ^= (1 << i);
                break;
            }
        }
        ans = min(ans, a1 - a - b + (a1|b1) + 1);
    }
    cout << ans << endl;
}

int main()
{
    ios::sync_with_stdio(0);

    int t;
    cin >> t;
    while (t --)
        solve();

    return 0;
}
C. Strange Test
题目简述:

​ 通过三种操作使 a 与 b 相等, 三种操作

  • a += 1
  • b += 1
  • a = a or b
思路:

​ 该题感性题解即好,首先需要明确的点为或操作仅且操作一次,不使用或时答案为 b - a, 使用时最优解一定需让a加一定次数,b加一定次数(加后设为 a1, b1),或操作后b1(a1|b1 > b1)在加一定次数,答案为(a1 - a) + (b1 - b) + ((a1|b1) - b1) + 1。代码思路为,枚举确定a1,当a1确定以后b1也可以确定(原因在于通过枚举二进制位,确定当且仅当 a1 和 b 的某二进制位分别为 1, 0),将b1变为…1000…,此时答案可记为: ( a 1 − a ) + ( b 1 − b ) + ( ( a 1 ∣ b 1 ) − b 1 + 1 ) (a1-a)+(b1-b)+((a1|b1)-b1 + 1) (a1a)+(b1b)+((a1b1)b1+1)

#include <bits/stdc++.h>

using namespace std;

void solve()
{
    int a, b;
    cin >> a >> b;

    // 初始答案设置为 b - a
    int ans = b - a;
    // 枚举 a1
    for (int a1 = a; a1 < b; a1 ++) {
        int b1 = 0;
        // 确定b1
        for (int i = 21; i >= 0; i --) {
            if ((b >> i) & 1) {
                // b1 二进制高位与 b 相同
                b1 ^= (1 << i);
            } else if ((a1 >> i) & 1){
                // b1 当前仅当a1该二进制位为0,b为1时更改,同时结束循环。
                b1 ^= (1 << i);
                break;
            }
        }
        ans = min(ans, a1 - a - b + (a1|b1) + 1);
    }
    cout << ans << endl;
}

int main()
{
    ios::sync_with_stdio(0);

    int t;
    cin >> t;
    while (t --)
        solve();

    return 0;
}
D.New Year Concert
题目简述:

给定长度为n的整数序列a, 通过改变任意 a i a_i ai 任意次数使得 g c d ( l , r )   ! = r − l + 1 ( 1 < = l < = r < = n ) gcd(l, r) \ != r - l + 1(1<=l<=r<=n) gcd(l,r) !=rl+1(1<=l<=r<=n) , 求1到第i段子序列最少需要改变的数的数量。

思路:

首先需要明确该题一定需要线段树维护区间gcd,区间gcd有一个性质为区间越长,gcd越小。首先明确暴力方法,去枚举由左及右枚举右端点,使以该端点为右端点的子区间满足 g c d ( l , r ) ! = r − l + 1 gcd(l,r) != r - l + 1 gcd(l,r)!=rl+1,显然,方法为将第 a[i] 更改为一个大素数,使任意gcd区间值为1(除去当前端点),更改完成后更新左端点即可。根据之前的gcd性质可推测出可用二分求解,二分区间到右端点的距离,gcd值随着距右端点距离缩短而增大,在二分前注意特判a[i] = 1 情况,必须更新当前值。

代码:
#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 7;

struct Node {
    int l, r, gcd;
} tr[N << 2];

int n;

int a[N];

void push_up(int u)
{
    tr[u].gcd = __gcd(tr[u << 1].gcd, tr[u << 1 | 1].gcd);
}

void build(int u, int l, int r)
{
    if (l == r) {
        tr[u] = {l, r, a[l]};
        return;
    }

    tr[u] = {l, r};
    int mid = l + r >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    push_up(u);
}

int query(int u, int L, int R)
{
    if (tr[u].l >= L && tr[u].r <= R)
        return tr[u].gcd;

    int mid = tr[u].l + tr[u].r >> 1, gcd = 0;
    if (L <= mid)
        gcd = query(u << 1, L, R);
    if (R > mid)
        gcd = __gcd(gcd, query(u << 1 | 1, L, R));
    
    return gcd;
}

bool lower_find(int l, int r)
{
    int m = r;
    while (l < r) {
        int mid = (l + r) >> 1;
        if (query(1, mid, m) < m - mid + 1)
            l = mid + 1;
        else 
            r = mid;
    }
    
    if (query(1, l, m) == m - l + 1) 
        return true;
    else 
        return false;
}

int main()
{
    ios::sync_with_stdio(0);

    cin >> n;
    for (int i = 1; i <= n; i ++)
        cin >> a[i];

    build(1, 1, n);

    int pre = 1;
    vector<int> ans(n + 1, 0);
    for (int i = 1; i <= n; i ++) {
        if (a[i] == 1) {
            ans[i] = ans[i - 1] + 1;
            pre = i + 1;
            continue;
        }
        bool flag = lower_find(pre, i);
        if (flag) {
            ans[i] = ans[i - 1] + 1;
            pre = i + 1;
        } else {
            ans[i] = ans[i - 1];
        }
    }

    for (int i = 1; i <= n; i ++) 
        cout << ans[i] << " ";
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值