Codeforces Round 943 (Div. 3) A~G1

A.Maximize?(枚举)

题意:

给你一个整数 x x x。你的任务是找出任意一个整数 y y y ( 1 ≤ y < x ) (1\le y\lt x) (1y<x),使得 gcd ⁡ ( x , y ) + y \gcd(x,y)+y gcd(x,y)+y为最大可能数。 ( 1 ≤ y < x ) (1\le y\lt x) (1y<x)使得 gcd ⁡ ( x , y ) + y \gcd(x,y)+y gcd(x,y)+y最大。

注意,如果有一个以上的 y y y满足该语句,允许找到任何一个。

gcd ⁡ ( a , b ) \gcd(a,b) gcd(a,b) a a a b b b的最大公约数。例如, gcd ⁡ ( 6 , 4 ) = 2 \gcd(6,4)=2 gcd(6,4)=2

分析:

本题数据范围较小,直接暴力枚举即可。

此外,因为 y < x y\lt x y<x所以 gcd ⁡ ( x , y ) \gcd(x,y) gcd(x,y)等价于 gcd ⁡ ( x , x − y ) \gcd(x,x-y) gcd(x,xy),易得 gcd ⁡ ( x , x − y ) ≤ x − y \gcd(x,x-y)\le x-y gcd(x,xy)xy,所以 gcd ⁡ ( x , x − y ) + y ≤ x \gcd(x,x-y)+y\le x gcd(x,xy)+yx,又因为相邻两个数的 gcd ⁡ \gcd gcd 1 1 1,所以当 y = x − 1 y=x-1 y=x1时一定满足题目,可以直接输出 x − 1 x-1 x1

代码:

#include<bits/stdc++.h>

using namespace std;

int gcd(int a, int b) {
    if (b > a) swap(a, b);
    if (b == 0) return a;
    return gcd(b, a % b);
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        int x;
        cin >> x;
        int res = 0;
        int ans = 1;
        for (int y = 1; y < x; y++) {
            if (gcd(x, y) + y > res) {
                res = gcd(x, y) + y;
                ans = y;
            }
        }
        cout << ans << endl;
    }
    return 0;
}

B.Prefiquence(双指针)

题意:

给你两个二进制字符串 a a a b b b。二进制字符串是由字符"0"和"1"组成的字符串。

你的任务是确定最大可能的数字 k k k,使得长度为 k k k的字符串 a a a的前缀是字符串 b b b的子序列。

如果 a a a可以从 b b b中删除几个(可能是零个或全部)元素,那么序列 a a a就是序列 b b b的子序列。

分析:

本题本质就是找 b b b字符串的子序列作为 a a a的前缀最大是多少。

考虑双指针,设置 l l l指针指向 a a a字符串的第一个字母 r r r指针指向 b b b字符串的第一个字母,若 l l l r r r指向的字母相同,则双指针往后移动,并更新 a n s ans ans值为 l l l指针 若 l l l r r r不相同 则移动 r r r指针至第一个可以和 l l l指针匹配的位置,逐个移动直到 l l l或者 r r r指针跑到 n n n m m m

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;

void solve() {
    LL n, m;
    cin >> n >> m;
    string a, b;
    cin >> a >> b;
    a = " " + a;
    b = " " + b;
    LL l, r;
    LL ans = 0;
    l = r = 1;
    while (l <= n && r <= m) {
        if (a[l] == b[r]) {
            l++;
            r++;
            ans = l;
        } else if (a[l] != b[r]) {
            while (a[l] != b[r] && r <= m) {
                r++;
            }
        }
    }
    ans = max(ans - 1, 0LL);
    cout << ans << endl;
}

int main() {
    LL t;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

C.Assembly via Remainders(构造)

题意:

给你一个数组 x 2 , x 3 , … , x n x_2,x_3,\dots,x_n x2,x3,,xn。你的任务是找出任意一个数组 a 1 , … , a n a_1,\dots,a_n a1,,an,其中:

  • 对于所有的 1 ≤ i ≤ n 1\le i\le n 1in 1 ≤ a i ≤ 1 0 9 1\le a_i\le 10^9 1ai109
  • 对于所有 2 ≤ i ≤ n 2\le i\le n 2in x i = a i   m o d   a i − 1 x_i=a_i\bmod a_{i-1} xi=aimodai1

这里的 c   m o d   d c\bmod d cmodd表示整数 c c c除以整数 d d d的余数。例如, 5   m o d   2 = 1 5\bmod 2=1 5mod2=1 72   m o d   3 = 0 72\bmod 3=0 72mod3=0 143   m o d   14 = 3 143\bmod 14=3 143mod14=3

注意,如果有一个以上的 a a a满足该语句的要求,你可以找到任何一个。

分析:

题目要求构造 a i a_i ai,考虑如果 a i − 1 a_{i-1} ai1 a i a_i ai大的话,根据模的计算方法,可以直接放入 x i x_i xi

我们每次构造出的 a i a_i ai,都让它等于 a i − 1 + x i a_{i-1}+x_i ai1+xi,让数组 a a a一直递增,即可符合要求。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const LL N = 505;
int x[N], a[N];

void solve() {
    int n;
    cin >> n;
    for (int i = 2; i <= n; i++)
        cin >> x[i];
    a[n] = 1e9;
    for (int i = n; i >= 2; i--) {
        if (x[i] == a[i]) {
            a[i - 1] = 1e9;
            continue;
        }
        a[i - 1] = a[i] - x[i];
    }
    for (int i = 1; i <= n; i++) {
        cout << a[i] << " ";
    }
    cout << endl;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

D.Permutation Game(贪心)

题意:

Bodya和Sasha发现了一个排列 p 1 , … , p n p_1,\dots,p_n p1,,pn和一个数组 a 1 , … , a n a_1,\dots,a_n a1,,an。他们决定玩一个著名的 “排列游戏”。

长度为 n n n的排列是由 n n n个不同的整数组成的数组,这些整数从 1 1 1 n n n按任意顺序排列。例如, [ 2 , 3 , 1 , 5 , 4 ] [2,3,1,5,4] [2,3,1,5,4]是一个排列,但 [ 1 , 2 , 2 ] [1,2,2] [1,2,2]不是一个排列( 2 2 2在数组中出现了两次), [ 1 , 3 , 4 ] [1,3,4] [1,3,4]也不是一个排列( n = 3 n=3 n=3,但数组中有 4 4 4)。

它们都在排列中选择了一个起始位置。

对局持续了 k k k个回合。棋手同时下棋。在每个回合中,每个棋手都会发生两件事:

  • 如果棋手当前的位置是 x x x,他的得分就会增加 a x a_x ax
  • 然后棋手要么停留在当前位置 x x x,要么 x x x移动到 p x p_x px

在整整 k k k个回合后,得分较高的一方即为获胜者。

知道了Bodya的起始位置 P B P_B PB和Sasha的起始位置 P S P_S PS后,如果两位棋手都想获胜,那么谁会赢得对局?

分析:

最优的方案一定是先走几步,然后一直停在某个点,如果我们站的点是最大的,那么我们就不需要移动,因为可以一直站在上面得到最大的分数。

否则我们就去下一个位置去看看,能不能获得更多的得分。

遍历的话因为是排列,移动是一个环,所以最多的遍历次数是 n n n

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;

void solve() {
    LL n, k, pb, ps;
    cin >> n >> k >> pb >> ps;
    vector<LL> p(n + 1), a(n + 1);
    for (int i = 1; i <= n; i++)
        cin >> p[i];
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    LL ans_pb = 0, ans_ps = 0;
    LL res_pb = 0, res_ps = 0;
    for (int i = 0; i < min(k, n); i++) {
        res_pb += a[pb];
        ans_pb = max(ans_pb, res_pb + (k - i - 1) * a[pb]);
        pb = p[pb];
        res_ps += a[ps];
        ans_ps = max(ans_ps, res_ps + (k - i - 1) * a[ps]);
        ps = p[ps];
    }
    if (ans_pb > ans_ps)
        cout << "Bodya" << endl;
    else if (ans_pb == ans_ps)
        cout << "Draw" << endl;
    else
        cout << "Sasha" << endl;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

E.Cells Arrangement(构造)

题意:

给你一个整数 n n n。您在网格 n × n n\times n n×n中选择 n n n个单元格 ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x n , y n ) (x_1,y_1),(x_2,y_2),\dots,(x_n,y_n) (x1,y1),(x2,y2),,(xn,yn),其中 1 ≤ x i ≤ n 1\le x_i\le n 1xin 1 ≤ y i ≤ n 1\le y_i\le n 1yin

H \mathcal{H} H为任意一对单元格之间不同的曼哈顿距离集合。你的任务是最大化 H \mathcal{H} H的大小。注释中给出了集合及其构造的例子。

如果存在不止一个解,你可以输出任意一个。

单元格 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) ( x 2 , y 2 ) (x_2,y_2) (x2,y2)之间的曼哈顿距离等于 ∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ |x_1-x_2|+|y_1-y_2| x1x2+y1y2

分析:

观察样例发现,在 ( 1 , 1 ) (1,1) (1,1)放一个,然后 ( 1 , 2 ) (1,2) (1,2)下面放一个,如果有多的,全部斜着放是能取完所有的距离情况的,这样放是最大值。

代码:

#include<bits/stdc++.h>

using namespace std;

void solve() {
    int n;
    cin >> n;
    cout << "1 1" << endl;
    cout << "1 2" << endl;
    for (int i = 3; i <= n; i++) {
        cout << i << " " << i << endl;
    }
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

F.Equal XOR Segments(前缀处理)

题意:

如果可以将数组分成 k > 1 k\gt 1 k>1部分,使得每一部分值按位异或都相等,那么我们就称这个数组为 x 1 , … , x m x_1,\dots,x_m x1,,xm有趣。

更正式地说,你必须把数组 x x x分成 k k k个连续的部分, x x x中的每个元素都必须准确地属于 1 1 1部分。设 y 1 , … , y k y_1,\dots,y_k y1,,yk分别是各部分元素的XOR。那么 y 1 = y 2 = ⋯ = y k y_1=y_2=\dots=y_k y1=y2==yk必须满足。

例如,如果是 x = [ 1 , 1 , 2 , 3 , 0 ] x=[1,1,2,3,0] x=[1,1,2,3,0],可以将其拆分如下: [ 1 ] , [ 1 ] , [ 2 , 3 , 0 ] [\color{blue}1],[\color{green}1],[\color{red}2,\color{red}3,\color{red}0] [1],[1],[2,3,0]。事实上是 1 = 1 = 2 ⊕ 3 ⊕ 0 \color{blue}1=\color{green}1=\color{red}2\oplus\color{red}3\oplus\color{red}0 1=1=230

给你一个数组 a 1 , … , a n a_1,\dots,a_n a1,,an。你的任务是回答 q q q次查询:

  • 对于固定的 l l l, r r r, 判断子数组 a l , a l + 1 , … , a r a_l,a_{l+1},\dots,a_r al,al+1,,ar是否有趣。

分析:

区间问题先转化为前缀异或。

  1. S r ⊕ S l − 1 = 0 S_r\oplus S_{l-1}=0 SrSl1=0

题目保证了 l < r l\lt r l<r,所以一定有解。

  1. S r ⊕ S l − 1 = v S_r\oplus S_{l-1}=v SrSl1=v

区间一定可以划分为奇数段。如果能划分成奇数段,则一定能划分成 3 3 3段。

最终划分的异或值一定是 v v v

于是问题转变为是否能把 [ l , r ] [l,r] [l,r]分成三段,使得每段异或值都为 v v v

找到最大的 i < r i\lt r i<r使得 S r ⊕ S i = v S_r\oplus S_i=v SrSi=v,如果找不到则无解。

找到最大的 j < i j\lt i j<i使得 S i ⊕ S j = v S_i\oplus S_j=v SiSj=v,如果找不到或则 j < l − 1 j\lt l-1 j<l1无解。

具体实现可以维护一个map<int, vector<int> >来实现。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;

void solve() {
    LL n, q;
    cin >> n >> q;
    vector<LL> a(n + 1);
    map<int, vector<int>> mp;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = 1; i <= n; i++) {
        a[i] ^= a[i - 1];
        mp[a[i]].push_back(i);
    }
    while (q--) {
        int l, r;
        cin >> l >> r;
        LL p = a[r] ^ a[l - 1];
        if (p == 0) {
            cout << "YES" << endl;
        } else {
            auto &v1 = mp[a[r]];
            auto pl = lower_bound(v1.begin(), v1.end(), l);
            if (pl == v1.end() || *pl >= r) {
                cout << "NO" << endl;
                continue;
            }
            LL posl = *pl;
            auto &v2 = mp[a[l - 1]];
            auto pr = lower_bound(v2.begin(), v2.end(), posl + 1);
            if (pr != v2.end() && *pr < r) {
                cout << "YES" << endl;
            } else
                cout << "NO" << endl;
        }
    }
}

int main() {
    int T;
    cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}

G1.Division + LCP (easy version)(LCP)

题意:

这是问题的简单版本。在这个版本中 l = r l=r l=r

给你一个字符串 s s s。对于固定的 k k k,考虑将 s s s恰好分成 k k k个连续的子串 w 1 , … , w k w_1,\dots,w_k w1,,wk。假设 f k f_k fk是所有分割中最大的 L C P ( w 1 , … , w k ) LCP(w_1,\dots,w_k) LCP(w1,,wk)

L C P ( w 1 , … , w m ) LCP(w_1,\dots,w_m) LCP(w1,,wm)是字符串 w 1 , … , w m w_1,\dots,w_m w1,,wm的最长公共前缀的长度。

例如,如果 s = a b a b a b c a b s=abababcab s=abababcab k = 4 k=4 k=4,可能的除法是 a b a b a b c a b \color{red}{ab}\color{blue}{ab}\color{orange}{abc}\color{green}{ab} abababcab。由于 a b ab ab是这四个字符串的最长公共前缀,因此 L C P ( a b , a b , a b c , a b ) LCP(\color{red}{ab},\color{blue}{ab},\color{orange}{abc},\color{green}{ab}) LCP(ab,ab,abc,ab)就是 2 2 2。请注意,每个子串都由一段连续的字符组成,每个字符都属于一个子串。

您的任务是找出 f l , f l + 1 , … , f r f_l,f_{l+1},\dots,f_r fl,fl+1,,fr。在此版本中为 l = r l=r l=r

分析:

如果存在划分使得 L C P LCP LCP v v v,则任意 v ‘ < v v \text{`} \lt v v<v都存在划分。

存在单调性,可以二分前缀长度。

令前缀字符串为 t t t s s s种最多有 x x x个不相交的子串 t t t

如果 x ≥ k x\ge k xk,则存在划分使得 L C P LCP LCP t t t

可以用 k m p kmp kmp或字符串哈希求出。

代码:

#include<bits/stdc++.h>

using namespace std;

void solve() {
    int n, _, k;
    string s;
    cin >> n >> _ >> k >> s;
    vector<int> ne(n + 1, 0);
    ne[0] = -1;
    for (int i = 1, j = -1; i < n; i++) {
        while (j >= 0 && s[j + 1] != s[i])
            j = ne[j];
        if (s[j + 1] == s[i])
            j++;
        ne[i] = j;
    }
    auto check = [&](int m) -> bool {
        if (m == 0)
            return 1;
        string t = s.substr(0, m);
        int tmp = 0;
        for (int i = 0, j = -1; i < n; i++) {
            while (j != -1 && s[i] != t[j + 1])
                j = ne[j];
            if (s[i] == t[j + 1])
                j++;
            if (j == m - 1) {
                tmp++;
                j = -1;
            }
        }
        return tmp >= k;
    };
    int l = 0, r = n / k;
    while (l < r) {
        int mid = l + r + 1 >> 1;
        if (check(mid))
            l = mid;
        else
            r = mid - 1;
    }
    cout << l << endl;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

赛后交流

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

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

Codeforces Round 894 (Div. 3) 是一个Codeforces举办的比赛,是第894轮的Div. 3级别比赛。它包含了一系列题目,其中包括题目E. Kolya and Movie Theatre。 根据题目描述,E. Kolya and Movie Theatre问题要求我们给定两个字符串,通过三种操作来让字符串a等于字符串b。这三种操作分别为:交换a中相同位置的字符、交换a中对称位置的字符、交换b中对称位置的字符。我们需要先进行一次预处理,替换a中的字符,然后进行上述三种操作,最终得到a等于b的结果。我们需要计算预处理操作的次数。 根据引用的讨论,当且仅当b[i]==b[n-i-1]时,如果a[i]!=a[n-i-1],需要进行一次操作;否则不需要操作。所以我们可以遍历字符串b的前半部分,判断对应位置的字符是否与后半部分对称,并统计需要进行操作的次数。 以上就是Codeforces Round 894 (Div. 3)的简要说明和题目E. Kolya and Movie Theatre的要求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Codeforces Round #498 (Div. 3) (A+B+C+D+E+F)](https://blog.csdn.net/qq_46030630/article/details/108804114)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Codeforces Round 894 (Div. 3)A~E题解](https://blog.csdn.net/gyeolhada/article/details/132491891)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值