Codeforces Round 965 (Div. 2) A~E1

A. Find K Distinct Points with Fixed Center (思维)

题意:

给你三个整数 x c x_c xc y c y_c yc k k k ( − 100 ≤ x c , y c ≤ 100 -100 \leq x_c, y_c \leq 100 100xc,yc100 , 1 ≤ k ≤ 1000 1 \leq k \leq 1000 1k1000 )。

你需要在 2 D 2D 2D坐标平面上找到具有整数坐标的 k k k 个不同点 ( x 1 , y 1 x_1, y_1 x1,y1 )、( x 2 , y 2 x_2, y_2 x2,y2 )、 … \ldots 、( x k , y k x_k, y_k xk,yk ),并且:

  • 它们的中心为 ( x c , y c x_c, y_c xc,yc )
  • 对于从 1 1 1 k k k 的所有 i i i − 1 0 9 ≤ x i , y i ≤ 1 0 9 -10^9 \leq x_i, y_i \leq 10^9 109xi,yi109

可以证明,至少有一组 k k k 个不同点始终存在,并且满足这些条件。

k k k 个点 ( x 1 , y 1 x_1, y_1 x1,y1 )、( x 2 , y 2 x_2, y_2 x2,y2 )、 … \ldots 、( x k , y k x_k, y_k xk,yk ) 的中心是 ( x 1 + x 2 + … + x k k , y 1 + y 2 + … + y k k ) \left( \frac{x_1 + x_2 + \ldots + x_k}{k}, \frac{y_1 + y_2 + \ldots + y_k}{k} \right) (kx1+x2++xk,ky1+y2++yk)

分析:

如果需要偶数个点,那我们就输出 k / 2 k/2 k/2个相对 ( x , y ) (x, y) (x,y)中心对称的点即可;如果需要奇数个点,再输出 ( x , y ) (x, y) (x,y)即可。

代码:

#include <bits/stdc++.h>

using namespace std;
const int mod = 998244353;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int x, y, k;
        cin >> x >> y >> k;
        if (k & 1) {
            cout << x << " " << y << endl;
            k--;
        }
        k /= 2;
        for (int i = 1; i <= k; i++) {
            cout << x + i << " " << y << endl;
            cout << x - i << " " << y << endl;
        }
    }
    return 0;
}

B. Minimize Equal Sum Subarrays (思维)

题意:

给定一个长度为 n n n 的排列 p p p

找到一个长度为 n n n 的排列 q q q ,使对数 ( i , j i, j i,j ) ( 1 ≤ i ≤ j ≤ n 1 \leq i \leq j \leq n 1ijn ) 最小化,使得 p i + p i + 1 + … + p j = q i + q i + 1 + … + q j p_i + p_{i+1} + \ldots + p_j = q_i + q_{i+1} + \ldots + q_j pi+pi+1++pj=qi+qi+1++qj

分析:

输出排列 p p p的循环左移一位或者循环右移一位的结果即可。只有 [ 1 , n ] [1,n] [1,n]这个区间和是相等的。

代码:

#include <bits/stdc++.h>

using namespace std;
const int mod = 998244353;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        vector<int> a(n);
        for (int &x: a)
            cin >> x;
        for (int i = 1; i < n; i++)
            cout << a[i] << " ";
        cout << a[0] << endl;
    }
    return 0;
}

C.Perform Operations to Maximize Score (二分)

题意:

你将获得一个长度为 n n n 的数组 a a a 和一个整数 k k k 。您还将获得一个长度为 n n n 的二进制数组 b b b

你最多可 ​​ 以执行以下操作 k k k 次:

  • 选择一个索引 i i i ( 1 ≤ i ≤ n 1 \leq i \leq n 1in ),并且 b i = 1 b_i = 1 bi=1 。设置 a i = a i + 1 a_i = a_i + 1 ai=ai+1 (即,将 a i a_i ai 增加 1 1 1 )。

你的得分定义为 max ⁡ i = 1 n ( a i + median ⁡ ( c i ) ) \max\limits_{i = 1}^{n} \left( a_i + \operatorname{median}(c_i) \right) i=1maxn(ai+median(ci)) ,其中 c i c_i ci 表示从 a a a 中删除 a i a_i ai 后得到的长度为 n − 1 n-1 n1 的数组。换句话说,你的得分是从 1 1 1 n n n 的所有 i i i a i + median ⁡ ( c i ) a_i + \operatorname{median}(c_i) ai+median(ci) 的最大值。
median ⁡ ( c i ) \operatorname{median}(c_i) median(ci)表示 c i c_i ci的中位数。
找出以最佳方式执行操作后可以获得的最高得分。

分析:

对于 b i = 1 b_i=1 bi=1位置,最优的操作是直接加上 a i a_i ai。而 b i = 0 b_i=0 bi=0的位置只能通过加别的数使得中位数变大。
我们先把数字按照 0 / 1 0/1 0/1进行分类,再考虑二分答案,判断能否从剩下的数字中找出 ⌈ n 2 ⌉ \lceil \frac{n}{2} \rceil 2n个大于 m i d mid mid的数字。对于 b i = 0 b_i=0 bi=0的位置,只需要二分一下还需要提供多少个数字即可。这样我们就能知道还需要 b i = 1 b_i=1 bi=1提供多少个数字。显然我们会选择 a i a_i ai最大的数字,再利用前缀和判断代价是否会超过 k k k即可。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
struct Point {
    LL x, y;
};

bool operator<(const Point &a, const Point &b) {
    return a.x < b.x || (a.x == b.x && a.y < b.y);
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n, k;
        cin >> n >> k;
        vector<Point> a(n + 1);
        for (int i = 1; i <= n; i++)
            cin >> a[i].x;
        for (int i = 1; i <= n; i++)
            cin >> a[i].y;
        sort(a.begin() + 1, a.end());
        LL ans = a[n / 2].x + a[n].x;
        LL tmp = 0;
        if (a[n].y == 1) {
            cout << ans + k << endl;
            continue;
        }
        for (int i = n; i >= 1; i--) {
            if (a[i].y) {
                tmp = i;
                break;
            }
        }
        LL l = a[n / 2].x, r = l + k;
        while (l <= r) {
            LL mid = (l + r) >> 1;
            LL all = n, cost = k;
            for (int i = n; i > 0 && all >= n / 2; i--) {
                if (a[i].x < mid && a[i].y) {
                    if (a[i].x + cost >= mid) {
                        cost -= mid - a[i].x;
                        all--;
                    }
                }
                if (a[i].x >= mid) {
                    all--;
                }
            }
            if (all < n / 2) {
                l = mid + 1;
                ans = max(ans, mid + a[n].x);
            } else {
                r = mid - 1;
            }
        }
        a[tmp].x += k;
        sort(a.begin() + 1, a.end());
        ans = max(ans, a[n / 2].x + a[n].x);
        cout << ans << endl;
    }
    return 0;
}

D.Determine Winning Islands in Race (dp)

题意:

农夫约翰的两头奶牛,贝西和埃尔西,计划在 n n n 个岛屿上赛跑。所有 1 ≤ i ≤ n − 1 1 \leq i \leq n - 1 1in1 岛上有 n − 1 n - 1 n1 座主桥,连接岛屿 i i i 和岛屿 i + 1 i + 1 i+1 。此外,还有 m m m 座备选桥。埃尔西可以使用主桥和备选桥,而贝西只能使用主桥。所有桥都是单向的,只能用于从索引较低的岛屿前往索引较高的岛屿。

最初,埃尔西从岛屿 1 1 1 出发,贝西从岛屿 s s s 出发。奶牛轮流转弯,贝西先转弯。假设奶牛在岛屿 i i i 上。在奶牛的回合中,如果有任何桥梁连接岛屿 i i i 和岛屿 j j j ,那么奶牛可以移动到岛屿 j j j 。然后,岛屿 i i i 倒塌,所有连接到岛屿 i i i 的桥梁也会倒塌。另外,请注意以下几点:

  • 如果没有桥梁连接岛屿 i i i 和另一个岛屿,那么岛屿 i i i 会倒塌,这头奶牛将被淘汰出局。
  • 如果另一头奶牛也在岛屿 i i i 上,那么在这头奶牛移动到另一个岛屿后,岛屿会倒塌,另一头奶牛将被淘汰出局。
  • 岛屿或桥梁倒塌后,任何奶牛都不能使用它们。
  • 如果一头奶牛被淘汰,则在比赛的剩余时间里,将跳过这头奶牛的回合。

一旦其中一头奶牛到达岛屿 n n n ,比赛就结束。可以证明,无论奶牛的策略如何,至少有一头奶牛会到达岛屿 n n n 。当且仅当 B e s s i e Bessie Bessie 先到达岛屿 n n n 时,她才会获胜。

对于每个 1 ≤ s ≤ n − 1 1 \leq s \leq n - 1 1sn1 ,确定如果 B e s s i e Bessie Bessie 从岛屿 s s s 开始比赛,她是否会获胜。假设两头奶牛都遵循最佳策略来确保各自的胜利。

分析:

我们用 f i f_i fi表示 E l s i e Elsie Elsie i i i的最少步数,那么当 E l s i e Elsie Elsie i i i且打算移动时, B e s s i e Bessie Bessie 已经移动到 S + f i + 1 S+f_i+1 S+fi+1,那么 E l s i e Elsie Elsie移动到 j j j,一定有 j > S + f ( i ) + 1 j >S+f(i)+1 j>S+f(i)+1才能赢,移项后得到 B e s s i e Bessie Bessie赢的条件是 S ≥ j − f ( i ) − 1 S \ge j-f(i)-1 Sjf(i)1,而不等号右边的式子在枚举 S S S时不断维护最大值即可。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n, m;
        cin >> n >> m;
        vector<LL> a(n + 1);
        vector<vector<LL>> g(n + 1);
        for (int i = 0; i < m; i++) {
            LL u, v;
            cin >> u >> v;
            a[u] = max(a[u], v);
            g[v].push_back(u);
        }
        vector<LL> f(n + 1, n);
        f[1] = 0;
        LL maxval = 0;
        for (int i = 1; i < n; i++) {
            if (i >= maxval)
                cout << 1;
            else
                cout << 0;

            f[i] = min(f[i], f[i - 1] + 1);
            for (auto j: g[i]) {
                f[i] = min(f[i], f[j] + 1);
            }
            maxval = max(maxval, a[i] - f[i] - 1);
        }
        cout << endl;
    }
    return 0;
}

E1.Eliminating Balls With Merging (Easy Version) (二分)

题意:

你有两个整数 n n n x x x ( x = n x=n x=n )。有 n n n 个球排成一排,从左到右编号为 1 1 1 n n n 。最初,在第 i i i 个球上写有一个值 a i a_i ai

对于从 1 1 1 n n n 的每个整数 i i i ,我们定义一个函数 f ( i ) f(i) f(i) ,如下所示:

  • 假设你有一个集合 S = { 1 , 2 , … , i } S = \{1, 2, \ldots, i\} S={1,2,,i}

  • 在每个操作中,您必须从 S S S 中选择一个整数 l l l ( 1 ≤ l < i 1 \leq l < i 1l<i ),使得 l l l 不是 S S S 中的最大元素。假设 r r r S S S 中大于 l l l 的最小元素。

  • 如果是 a l > a r a_l > a_r al>ar ,则设置 a l = a l + a r a_l = a_l + a_r al=al+ar 并从 S S S 中删除 r r r

  • 如果是 a l < a r a_l < a_r al<ar ,则设置 a r = a l + a r a_r = a_l + a_r ar=al+ar 并从 S S S 中删除 l l l

  • 如果是 a l = a r a_l = a_r al=ar ,则选择从 S S S 中删除整数 l l l r r r

  • 如果选择从 S S S 中删除 l l l ,则设置 a r = a l + a r a_r = a_l + a_r ar=al+ar 并从 S S S 中删除 l l l

  • 如果选择从 S S S 中删除 r r r ,则设置 a l = a l + a r a_l = a_l + a_r al=al+ar 并从 S S S 中删除 r r r

  • f ( i ) f(i) f(i) 表示整数 j j j ( 1 ≤ j ≤ i 1 \le j \le i 1ji ) 的数量,这样在执行上述操作恰好 i − 1 i - 1 i1 次后,可以获得 S = { j } S = \{j\} S={j}

对于从 x x x n n n 的每个整数 i i i ,你需要找到 f ( i ) f(i) f(i)

分析:

我们发现最优操作肯定是从位置 i i i开始,对于左右,能吃掉就尽量吃掉,再判断能否吃掉所有数字。如果一个一个吃的话,那每次模拟复杂度为 O ( n ) O(n) O(n),不可以接受。我们假设某个时刻已经吃掉的范围为 [ l , r ] [l,r] [l,r]。在这个过程中,我们可以每次向左向右找到第一个大于当前值的数字,然后判断能否能否吃掉这个数字。可以发现,每次多吃一个数字后当前值至少乘 2 2 2,这个过程最多进行 l o g log log次。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
const int mod = 998244353;
int n, E, a[N], ls[N], rs[N], top, tmp[N];
LL s1[N], s2[N];

void dfs(int x) {
    if (!x)
        return;
    dfs(ls[x]), dfs(rs[x]);
    s1[x] = s1[ls[x]] + s1[rs[x]] + a[x];
    s2[x] = 1;
    if (s1[ls[x]] >= a[x])
        s2[x] += s2[ls[x]];
    if (s1[rs[x]] >= a[x])
        s2[x] += s2[rs[x]];
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        top = 0;
        cin >> n >> E;
        for (int i = 1; i <= n; i++) {
            ls[i] = rs[i] = 0;
            cin >> a[i];
            while (top && a[tmp[top]] <= a[i])
                ls[i] = tmp[top],
                        top--;
            if (top)
                rs[tmp[top]] = i;
            tmp[++top] = i;
        }
        dfs(tmp[1]);
        cout << s2[tmp[1]] << endl;
    }
    return 0;
}1

赛后交流

在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。

群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

  • 24
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值