【Codeforces Round #830 (Div. 2)】【A-D题题解】

本文介绍了四个关于数组和字符串操作的算法问题,包括使数组最大公除数为1的最小成本、使二进制字符串不递减的最小操作数、求最大子段代价以及在给定范围内找最大子段代价的算法。每个问题都提供了思路和代码实现,涉及到了数学、位操作和动态规划等技巧。
摘要由CSDN通过智能技术生成

A. Bestie

题目大意:

给你一个由n个整数a1,a2,…,an组成的数组a。朋友要求你使数组中所有数字的最大公除数(GCD)等于1。 在一次操作中,你可以做以下事情。

在数组中选择一个任意的索引1≤i≤n。
使ai=gcd(ai,i),其中gcd(x,y)表示整数x和y的GCD.这种操作的成本是n-i+1.
你需要找到我们需要执行的最小的操作总成本,使所有数组数字的GCD变成等于1。

输入
每个测试由多个测试案例组成。第一行包含一个整数t(1≤t≤5000)–测试案例的数量。测试用例的描述如下。

每个测试用例的第一行包含一个整数n(1≤n≤20)–数组的长度。

每个测试用例的第二行包含n个整数a1,a2,…,an(1≤ai≤10^9)–数组的元素。

输出
对于每个测试用例,输出一个整数–为了使数组中所有数字的GCD等于1而需要执行的最小总运算成本。

我们可以证明,总是有可能做到的。

思路:

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<pair<int,int>> vpii;
#define all(a) a.begin(), a.end()
#define nl '\n'
#define debug() cout << "debug:"
const int inf = 0x3f3f3f3f;
const int maxn = 2e5+5;
void solve(){
    int n, t, x;
    scanf("%d", &n);
    int d = 0;
    for (int i = 1; i <= n; i++){
        int x;
        scanf("%d", &x);
        d = __gcd(d, x);
    }
    if(d == 1){
        puts("0");
        return;
    }
    if(__gcd(d, n) == 1)
        puts("1");
    else if(__gcd(d, n - 1) == 1)
        puts("2");
    else
        puts("3");
}

int main(){
    //ios::sync_with_stdio(0);
    //freopen("in", "r", stdin);
    int t = 1;
    cin>>t;
    while(t--)
        solve();
    return 0;
}

B. Ugu

题目大意:

二进制字符串是指仅由字符0和1组成的字符串,给你一个二进制字符串s1s2…sn。有必要使这个字符串在最少的操作中不递减。换句话说,每个字符都不能少于前一个。在一次操作中,你可以做以下工作。

在字符串中选择一个任意的索引1≤i≤n。
对于所有的j≥i,将第j个位置的值改为相反的值,也就是说,如果sj=1,则使sj=0,反之亦然。
要使该字符串不递减所需的最少操作数是多少?

输入
每个测试由多个测试案例组成。第一行包含一个整数t(1≤t≤10^4)–测试案例的数量。测试用例的描述如下。

每个测试用例的第一行包含一个整数n(1≤n≤10^5)–字符串的长度。

每个测试案例的第二行包含一个长度为n的二进制字符串s。

保证所有测试用例的n之和不超过2⋅10^5。

输出
对于每个测试用例,输出一个整数–使字符串不递减所需的最小操作数。

思路:

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<pair<int,int>> vpii;
#define all(a) a.begin(), a.end()
#define nl '\n'
#define debug() cout << "debug:"
const int inf = 0x3f3f3f3f;
const int maxn = 2e5+5;
void solve(){
    int t, n, f[maxn] = {0};
    char s[maxn];
    scanf("%d%s", &n, s + 1);
    s[0] = '0';
    for (int i = 1; i <= n; i++)
        f[i] = f[i - 1] + (s[i] != s[i - 1]);
    printf("%d\n", max(f[n] - 1, 0));
}

int main(){
    //ios::sync_with_stdio(0);
    //freopen("in", "r", stdin);
    int t = 1;
    cin>>t;
    while(t--)
        solve();
    return 0;
}

C1. Sheikh (Easy version)

题目大意:

这是该问题的简单版本。唯一不同的是,在这个版本中,q=1。

给你一个整数数组a1,a2,…,an。

数组[l,r]的一个子段的成本,1≤l≤r≤n,是值f(l,r)=sum(l,r)-xor(l,r),其中sum(l,r)=al+al+1+…+ar,xor(l,r)=al⊕al+1⊕…⊕ar(⊕代表位向XOR)。

你将有q=1个查询。每个查询由一对数字Li, Ri给出,其中1≤Li≤Ri≤n。你需要找到具有最大值f(l,r)的子段[l,r],Li≤l≤r≤Ri。如果有几个答案,那么你需要在其中找到一个长度最小的子段,也就是r-l+1的最小值。

输入
每个测试由多个测试案例组成。第一行包含一个整数t(1≤t≤10^4)–测试案例的数量。测试用例的描述如下。

每个测试用例的第一行包含两个整数n和q (1≤n≤10^5, q=1) - 阵列的长度和查询次数。

每个测试案例的第二行包含n个整数a1,a2,…,an(0≤ai≤10^9)–数组元素。

每个测试案例接下来的q行中的第i行包含两个整数Li和Ri(1≤Li≤Ri≤n)–我们需要在其中找到段的边界。

保证所有测试案例的n之和不超过2⋅105。

保证L1=1,R1=n。

输出
对于每个测试案例,打印q对数字Li≤l≤r≤Ri,使f(l,r)的值最大,并且其中r-l+1的长度最小。如果有几个正确的答案,打印其中任何一个。

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<pair<int, int>> vpii;
#define all(a) a.begin(), a.end()
#define nl '\n'
#define debug() cout << "debug:"
const int inf = 0x3f3f3f3f;
const int N = 2e5 + 5;
ll s[N], x[N];
int nonz_left[N], nonz_right[N], a[N];
ll f(int l, int r){
    return s[r] - s[l - 1] - (x[r] ^ x[l - 1]);
}
void solve(){
    int n, q;
    cin >> n >> q;
    for (int i = 1; i <= n; i++){
        cin >> a[i];
        s[i] = s[i - 1] + a[i];
        x[i] = x[i - 1] ^ a[i];
        nonz_left[i] = a[i] == 0 ? nonz_left[i - 1] : i;
    }
    nonz_right[n + 1] = n + 1;
    for (int i = n; i >= 1; i--)
        nonz_right[i] = a[i] == 0 ? nonz_right[i + 1] : i;
    while (q--){
        int L, R;
        cin >> L >> R;
        int l = L, r = R;
        ll ans = f(l, r);
        if (ans == 0){
            cout << l << ' ' << l << '\n';
            continue;
        }
        int i = L;
        while (i <= R and f(i, R) == ans){
            int j = R;
            while (j >= i){
                if (f(i, j) == ans){
                    if (j - i < r - l){
                        l = i;
                        r = j;
                    }
                }
                else
                    break;
                j = nonz_left[j - 1];
            }
            i = nonz_right[i + 1];
        }
        cout << l << ' ' << r << '\n';
    }
}
int main(){
    // ios::sync_with_stdio(0);
    // freopen("in", "r", stdin);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

C2. Sheikh (Hard Version)

题目大意:

这是该问题的困难版本。唯一不同的是,在这个版本中q=n。

你将得到一个整数数组a1,a2,…,an。

数组[l,r]的一个子段的成本,1≤l≤r≤n,是值f(l,r)=sum(l,r)-xor(l,r),其中sum(l,r)=al+al+1+…+ar,xor(l,r)=al⊕al+1⊕…⊕ar(⊕代表位向XOR)。

你将有q个查询。每个查询由一对数字Li, Ri给出,其中1≤Li≤Ri≤n。你需要找到具有最大值f(l,r)的子段[l,r],Li≤l≤r≤Ri。如果有几个答案,那么你需要在其中找到一个长度最小的子段,也就是r-l+1的最小值。

输入
每个测试由多个测试案例组成。第一行包含一个整数t(1≤t≤10^4)–测试案例的数量。测试用例的描述如下。

每个测试用例的第一行包含两个整数n和q(1≤n≤10^5,q=n)–阵列的长度和查询次数。

每个测试用例的第二行包含n个整数a1,a2,…,an (0≤ai≤10^9) - 数组元素。

每个测试案例接下来的q行中的第i行包含两个整数Li和Ri(1≤Li≤Ri≤n)–我们需要在其中找到段的边界。

保证所有测试案例的n之和不超过2⋅10^5。

保证L1=1,R1=n。

输出
对于每个测试案例,打印q对数字Li≤l≤r≤Ri,使f(l,r)的值最大,并且其中r-l+1的长度最小。如果有几个正确的答案,打印其中任何一个。

code :

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<pair<int, int>> vpii;
#define all(a) a.begin(), a.end()
#define nl '\n'
#define debug() cout << "debug:"
const int inf = 0x3f3f3f3f;
const int N = 2e5 + 5;
ll s[N], x[N];
int nonz_left[N], nonz_right[N], a[N];
ll f(int l, int r){
    return s[r] - s[l - 1] - (x[r] ^ x[l - 1]);
}
void solve(){
    int n, q;
    cin >> n >> q;
    for (int i = 1; i <= n; i++){
        cin >> a[i];
        s[i] = s[i - 1] + a[i];
        x[i] = x[i - 1] ^ a[i];
        nonz_left[i] = a[i] == 0 ? nonz_left[i - 1] : i;
    }
    nonz_right[n + 1] = n + 1;
    for (int i = n; i >= 1; i--)
        nonz_right[i] = a[i] == 0 ? nonz_right[i + 1] : i;
    while (q--){
        int L, R;
        cin >> L >> R;
        int l = L, r = R;
        ll ans = f(l, r);
        if (ans == 0){
            cout << l << ' ' << l << '\n';
            continue;
        }
        int i = L;
        while (i <= R and f(i, R) == ans){
            int j = R;
            while (j >= i){
                if (f(i, j) == ans){
                    if (j - i < r - l){
                        l = i;
                        r = j;
                    }
                }
                else
                    break;
                j = nonz_left[j - 1];
            }
            i = nonz_right[i + 1];
        }
        cout << l << ' ' << r << '\n';
    }
}
int main(){
    // ios::sync_with_stdio(0);
    // freopen("in", "r", stdin);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eternity_GQM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值