Codeforces Round #768 (Div. 2)

A Min Max Swap

题意

给定长度为n的数组a和b,可以进行如下任意次数的操作:

选择一个下标 i ( 1 ≤ i ≤ n ) i (1 \le i \le n ) i(1in), 交换 a [ i ] a[i] a[i] b [ i ] b[i] b[i]

求经过任意次数的操作后, m a x ( a 1 , a 2 , . . . , a n ) ∗ m a x ( b 1 , b 2 , . . . , b n ) max(a_1, a_2, ..., a_n) * max(b_1, b_2, ..., b_n) max(a1,a2,...,an)max(b1,b2,...,bn) 的最小值

思路

可以发现a,b中最大的元素对于乘积必然会有贡献,要使得乘积最小,就要使得最大的元素不在另一个数组的最大值尽可能的小

故我们可以选择所有的 a [ i ] > b [ i ] a[i] \gt b[i] a[i]>b[i] i i i,交换二者的值,答案即为交换后的两个数组的最大值相乘

code


#include <iostream>

#include <cstdio>

#include <algorithm>

#include <cstring>

#include <vector>

#include <queue>

#include <stack>

#include <unordered_map>

#include <unordered_set>




using namespace std;

using ll = long long;




const int N = 110;

int n;

int a[N], b[N];




void solve() {

    scanf("%d", &n);

    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);

    for(int i = 1; i <= n; i++) scanf("%d", &b[i]);

    int maxv_a = 0, maxv_b = 0;

    for(int i = 1; i <= n; i++) {

        if(a[i] > b[i]) swap(a[i], b[i]);

        maxv_a = max(maxv_a, a[i]);

        maxv_b = max(maxv_b, b[i]);

    }

    printf("%d\n", maxv_a * maxv_b);

}




int main() {

    int t;

    scanf("%d", &t);

    while(t--) solve();

    return 0;

}

B. Fun with Even Subarrays

题意

给定一个长度为n的数组,可以进行如下的操作:

从数组中选择一个从l开始,长度为2k的子数组 ( 1 ≤ l ≤ l + 2 ∗ k − 1 ≤ n , k ≥ 1 ) (1 \le l \le l + 2 * k - 1 \le n, k \ge 1) (1ll+2k1n,k1), 对于每一个 i ( 0 ≤ i ≤ k − 1 ) i(0 \le i \le k - 1) i(0ik1),令 a l + i = a l + k + i a_{l + i} = a_{l + k + i} al+i=al+k+i

求使得数组的所有元素相等的最小操作次数

思路

操作实际上就是选择一个长度为2k的子数组,将后一半的数组赋值到前一半的数组中

可以发现数组中的最后一个元素是无法修改成其他元素的,那么数组最后必然全部为最后一个元素的值

我们可以从最后第二个个元素开始,如果当前元素 a [ i ] a[i] a[i]和后面的元素(也就是最后一个元素)不相等的话,令 k = n − i k = n - i k=ni,进行一次操作,操作之后 i = i − k i = i - k i=ik, 直到 i < 1 i < 1 i<1

code


#include <iostream>

#include <cstdio>

#include <algorithm>

#include <cstring>

#include <vector>

#include <queue>

#include <stack>

#include <unordered_map>

#include <unordered_set>


using namespace std;

using ll = long long;


const int N = 2e5 + 10;

int n;

int a[N];


void solve() {

    scanf("%d", &n);

    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);

    int res = 0;

    int j = n - 1;

    while(true) {

        while(j >= 1 && a[j] == a[n]) j--;

        if(j < 1) break;

        res++;

        int len = n - j;

        j = j - len;

    }

    printf("%d\n", res);

}


int main() {

    int t;

    scanf("%d", &t);

    while(t--) solve();

    return 0;

}

C. And Matching

题意

给定一个n(n为2的整数次幂)以及k

求长度为 n / 2 n / 2 n/2的数组a和b, a和b中的元素为0,1,2,…n - 1, 每一个数字只能出现一次

并且 ∑ i = 1 n / 2 a i   a n d   b i = k \sum_{i=1}^{n/2} a_{i} \, and \, b_{i} = k i=1n/2aiandbi=k

思路

假如 n = 8, k = 0, 构造如下:

a 1 = 00 0 2 b 1 = 11 1 2 a_{1} = 000_{2} \quad b_{1} = 111_{2} a1=0002b1=1112

a 2 = 00 1 2 b 2 = 11 0 2 a_{2} = 001_{2} \quad b_{2} = 110_{2} a2=0012b2=1102

a 3 = 01 0 2 b 3 = 10 1 2 a_{3} = 010_{2} \quad b_{3} = 101_{2} a3=0102b3=1012

a 4 = 01 1 2 b 4 = 10 0 2 a_{4} = 011_{2} \quad b_{4} = 100_{2} a4=0112b4=1002

1 < k < n 1 \lt k \lt n 1<k<n时, 只需要将 00 0 2 000_2 0002与对应k的所在的位置交换即可

k = = n − 1 k == n - 1 k==n1 时(n = 4时无解), 只需要将 a 1 , b 2 , a 4 a_1, b_2, a_4 a1,b2,a4的值调换一下即可

code


#include <iostream>

#include <cstdio>

#include <algorithm>

#include <cstring>

#include <vector>

#include <queue>

#include <stack>

#include <unordered_map>

#include <unordered_set>



using namespace std;

using ll = long long;




const int N = 1e6;

int n, k;

int a[N];


void solve() {

    scanf("%d%d", &n, &k);

    if(k == n - 1 && n == 4) return puts("-1"), void();

    int len = n / 2;

    for(int i = 1; i <= n; i++) {

        a[i] = i - 1;

    }

    if(k == n - 1) {

        swap(a[1], a[n - 1]);

        swap(a[n - 1], a[4]);

    } else {

        swap(a[1], a[k + 1]);

    }


    for(int i = 1; i <= len; i++) {

        printf("%d %d\n", a[i], a[n - i + 1]);

    }

}



int main() {

    int t;

    scanf("%d", &t);

    while(t--) solve();

    return 0;

}

D. Range and Partition

题意

给定一个长度为n的数组a,找到一个范围[x, y], 将数组a分割成k个连续子数组( 1 ≤ k ≤ n 1 \le k \le n 1kn), 并且每一个子数组中的元素在[x, y]范围内的数量要严格大于不在[x, y]范围内的数量(在范围内是指 x ≤ a i ≤ y x \le a_i \le y xaiy)

求最小的y - x, 输出x, y以及分割后的k的数组

思路

  1. 假设已经给定[x, y],如何判断[x, y]是否满足要求并且找到对应分割的数组

构造数组b, b i = [ x ≤ a i ≤ y ] ? 1 : − 1 b_i = [x \le a_i \le y] ? 1 : -1 bi=[xaiy]?1:1

数组sum为数组b的前缀和数组

每一个子数组中的元素在[x, y]范围内的数量要严格大于不在[x, y]范围内的数量等价于数组a中在[x, y]范围内的数量减去不[在x, y]范围内的数量 ≥ k \ge k k, 同时也等价于 s u m n ≥ k sum_n \ge k sumnk

构造分割的数组:

令f[x]为x第一次在前缀数组sum出现的下标 ( 1 ≤ x ≤ k ) (1 \le x \le k) (1xk)

按照f[1], f[2], … f[k-1] 进行分割数组即可

  1. 那么对于每一个x, 可以通过双指针或者二分的方式找到最小的y

code

#include <iostream>

#include <cstdio>

#include <algorithm>

#include <cstring>

#include <vector>

#include <queue>

#include <stack>

#include <unordered_map>

#include <unordered_set>


using namespace std;

using pii = pair<int, int>;

using ll = long long;


const int N = 2e5 + 10;

int n, k;

int a[N];


void solve() {

    scanf("%d%d", &n, &k);

    unordered_map<int, int> mp;

    for(int i = 1; i <= n; i++) {

        scanf("%d", &a[i]);

        mp[a[i]]++;

    }

    vector<pii> cnt;

    for(auto it : mp) {

        cnt.push_back({it.first, it.second});

    }

    sort(cnt.begin(), cnt.end());

    pii res = {1, n + 1};

    int sum = 0; // x <= a[i] <= y的数量

    for(int i = 0, j = 0; i < cnt.size(); i++) {

        while(j < cnt.size() && sum * 2 - n < k) {

            sum += cnt[j].second;

            j++;

        }
        // sum * 2 - n = sum - (n - sum) >= k
        // 范围内的数量 - 不在范围内的数量 要大于等于 k

        if(sum * 2 - n >= k && cnt[j - 1].first - cnt[i].first < res.second - res.first) {

            res = {cnt[i].first, cnt[j - 1].first};

        }

        sum -= cnt[i].second;

    }

    printf("%d %d\n", res.first, res.second);

    int idx = 1;

    sum = 0;

    for(int i = 1, last = 1; i <= n; i++) {

        if(a[i] >= res.first && a[i] <= res.second) sum++;

        else sum--;

        if(sum == idx) {

            if(idx == k) {

                printf("%d %d\n", last, n);

                break;

            } else {

                printf("%d %d\n", last, i);

                last = i + 1;

                idx++;

            }

        }

    }

}



int main() {

    int t;

    scanf("%d", &t);

    while(t--) solve();

    return 0;

}

E. Paint the Middle

题意

给定长度为n的数组a和c, 开始时所有的 c i = 0 c_i = 0 ci=0, 并且 1 ≤ a i ≤ n 1 \le a_i \le n 1ain

可以对数组进行如下的操作:

选择三个下标 i , j , k ( 1 ≤ i < j < k ≤ n ) , 并 且 C i = c j = c k = 0 , a i = a k i, j, k(1 \le i \lt j \lt k \le n), 并且C_i = c_j = c_k = 0, a_i = a_k i,j,k(1i<j<kn),Ci=cj=ck=0,ai=ak, 然后设置 c j = 1 c_j = 1 cj=1

求进行一系列操作后 ∑ i = 1 n c i \sum_{i = 1}^{n}c_i i=1nci可能的最大值

思路

只有出现两次以上的数字才可能满足上述操作要求的i和k, 并且出现大于两次的数字来说,选择第一个和最后一个是最优的

使用l[i]和r[i]记录a[i]出现的第一个和最后一个位置

对于每一段[L, R], 能够做出的贡献为max(R - L - 1, 0)

并且如果存在相交的线段, 即 i ∈ [ L + 1 , R − 1 ] 的 r [ a [ i ] ] > R i \in [L + 1, R - 1]的r[a[i]] \gt R i[L+1,R1]r[a[i]]>R则[R, r[a[i]]的贡献为r[a[i]] - RR - 1

code

#include <iostream>

#include <cstdio>

#include <algorithm>

#include <cstring>

#include <vector>

#include <queue>

#include <stack>

#include <unordered_map>

#include <unordered_set>


using namespace std;

using ll = long long;


const int N = 2e5 + 10;

int n;

int a[N], l[N], r[N];


void solve() {

    scanf("%d", &n);

    for(int i = 1; i <= n; i++) {

        scanf("%d", &a[i]);

        if(!l[a[i]]) l[a[i]] = i;

        r[a[i]] = i;

    }

    int L = 1, R = r[a[1]];

    int res = max(R - L - 1, 0);

    while(L <= n) {

        int maxv = 0;

        for(int i = L + 1; i < R; i++) maxv = max(maxv, r[a[i]]);

        if(maxv > R) {

            res += max(maxv - R - 1, 0);

            L = R;

            R = maxv;

        } else {

            L = R + 1;

            R = r[a[L]];

            res += max(R - L - 1, 0);

        }

    }

    printf("%d\n", res);

}


int main() {

    int t = 1;

    // scanf("%d", &t);

    while(t--) solve();

    return 0;

}

F. Flipping Range

题意

给定一个长度为n的数组a, 以及一个大小为m的集合b ( 1 ≤ m ≤ ⌊ n / 2 ⌋ , 1 ≤ b i ≤ ⌊ n / 2 ⌋ ) (1 \le m \le \lfloor n / 2 \rfloor, 1 \le b_i \le \lfloor n / 2 \rfloor) (1mn/2,1bin/2)

每次选择一个x ( x ∈ b ) (x \in b) (xb)以及一段长度为x的连续子数组,将这段数组的所有值乘以-1

求进行若干次操作之后 ∑ i = 1 n a i \sum_{i=1}^{n}a_i i=1nai的最大值

思路

  1. 考虑可以进行操作的最小的长度为多少

假设 x ∈ b   a n d   y ∈ b x \in b \, and \, y \in b xbandyb, 则可以通过操作一次长度的x以及一次长度为y, 使得得到一次操作长度为x - y的效果, 由gcd(a, b) = gcd(b, a - b), 可得最小的操作长度为 g = g c d ( b 1 , b 2 , . . . b m ) g = gcd(b_1, b_2,...b_m) g=gcd(b1,b2,...bm)

问题等价于每次进行一次长度为g的操作, 使得 ∑ i = 1 n a i \sum_{i=1}^{n}a_i i=1nai的值最大

  1. 令s为长度为n的数组, 当 s i s_i si为0时表示 a i a_i ai没有乘以-1, 当 s i s_i si为1时表示 a i a_i ai乘以了-1, 对所有的 s i s_i si按照 i m o d g i \quad mod \quad g imodg进行分组, 将同一组的值进行异或得到 f x ( x 为 i m o d g ) f_x(x为i \quad mod \quad g) fx(ximodg)

一共分成了g组, 每一个操作只会对组内的一个元素进行异或, 则所有 f x f_x fx要么同时等于1, 要么同时等于0

考虑dp:

d p [ i ] [ 0 ] 为 ∑ k = 0 i − k ∗ g ≥ 0 a i − k ∗ g 并 且 f i m o d g = 0 的 最 大 值 dp[i][0]为\sum_{k = 0}^{i - k * g \ge 0}a_{i - k * g} 并且f_{i \quad mod \quad g} = 0的最大值 dp[i][0]k=0ikg0aikgfimodg=0

d p [ i ] [ 1 ] 为 ∑ k = 0 i − k ∗ g ≥ 0 a i − k ∗ g 并 且 f i m o d g = 1 的 最 大 值 dp[i][1]为\sum_{k = 0}^{i - k * g \ge 0}a_{i - k * g} 并且f_{i \quad mod \quad g} = 1的最大值 dp[i][1]k=0ikg0aikgfimodg=1

code

#include <iostream>

#include <cstdio>

#include <algorithm>

#include <cstring>

#include <vector>

#include <queue>

#include <stack>

#include <unordered_map>

#include <unordered_set>


using namespace std;

using ll = long long;


const int N = 1e6 + 10;

int n, m, b;

ll a[N];

ll dp[N][2];


void solve() {

    scanf("%d%d", &n, &m);

    for(int i = 0; i <= n; i++) dp[i][0] = 0, dp[i][1] = -2e9;

    for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);

    int g = 0, b;

    for(int i = 1; i <= m; i++) {

        scanf("%d", &b);

        g = __gcd(g, b);

    }

    for(int i = 1; i <= n; i++) {

        int mod = i % g;

        ll v0 = max(dp[mod][0] + a[i], dp[mod][1] - a[i]);

        ll v1 = max(dp[mod][1] + a[i], dp[mod][0] - a[i]);

        dp[mod][0] = v0;

        dp[mod][1] = v1;

    }

    ll sum0 = 0, sum1 = 0;

    for(int i = 0; i < g; i++) sum0 += dp[i][0], sum1 += dp[i][1];

    printf("%lld\n", max(sum0, sum1));

}


int main() {

    int t;

    scanf("%d", &t);

    while(t--) solve();

    return 0;

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值