Codeforces Round 965 (Div. 2) A~E1

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

题意:

给你三个整数 xcx_cxcycy_cyckkk ( −100≤xc,yc≤100-100 \leq x_c, y_c \leq 100100xc,yc100 , 1≤k≤10001 \leq k \leq 10001k1000 )。

你需要在2D2D2D坐标平面上找到具有整数坐标的 kkk 个不同点 ( x1,y1x_1, y_1x1,y1 )、( x2,y2x_2, y_2x2,y2 )、 …\ldots 、( xk,ykx_k, y_kxk,yk ),并且:

  • 它们的中心为 ( xc,ycx_c, y_cxc,yc )
  • 对于从 111kkk 的所有 iii−109≤xi,yi≤109-10^9 \leq x_i, y_i \leq 10^9109xi,yi109

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

kkk 个点 ( x1,y1x_1, y_1x1,y1 )、( x2,y2x_2, y_2x2,y2 )、 …\ldots 、( xk,ykx_k, y_kxk,yk ) 的中心是 (x1+x2+…+xkk,y1+y2+…+ykk)\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/2k/2k/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 (思维)

题意:

给定一个长度为 nnn 的排列 ppp

找到一个长度为 nnn 的排列 qqq ,使对数 ( i,ji, ji,j ) ( 1≤i≤j≤n1 \leq i \leq j \leq n1ijn ) 最小化,使得 pi+pi+1+…+pj=qi+qi+1+…+qjp_i + p_{i+1} + \ldots + p_j = q_i + q_{i+1} + \ldots + q_jpi+pi+1++pj=qi+qi+1++qj

分析:

输出排列ppp的循环左移一位或者循环右移一位的结果即可。只有[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 (二分)

题意:

你将获得一个长度为 nnn 的数组 aaa 和一个整数 kkk 。您还将获得一个长度为 nnn 的二进制数组 bbb

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

  • 选择一个索引 iii ( 1≤i≤n1 \leq i \leq n1in ),并且 bi=1b_i = 1bi=1 。设置 ai=ai+1a_i = a_i + 1ai=ai+1 (即,将 aia_iai 增加 111 )。

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

分析:

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

代码:

#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)

题意:

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

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

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

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

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

分析:

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

代码:

#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) (二分)

题意:

你有两个整数 nnnxxx ( x=nx=nx=n )。有 nnn 个球排成一排,从左到右编号为 111nnn 。最初,在第 iii 个球上写有一个值 aia_iai

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

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

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

  • 如果是 al>ara_l > a_ral>ar ,则设置 al=al+ara_l = a_l + a_ral=al+ar 并从 SSS 中删除 rrr

  • 如果是 al<ara_l < a_ral<ar ,则设置 ar=al+ara_r = a_l + a_rar=al+ar 并从 SSS 中删除 lll

  • 如果是 al=ara_l = a_ral=ar ,则选择从 SSS 中删除整数 lllrrr

  • 如果选择从 SSS 中删除 lll ,则设置 ar=al+ara_r = a_l + a_rar=al+ar 并从 SSS 中删除 lll

  • 如果选择从 SSS 中删除 rrr ,则设置 al=al+ara_l = a_l + a_ral=al+ar 并从 SSS 中删除 rrr

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

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

分析:

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

代码:

#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,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值