Codeforces Round 943 (Div. 3) A~G1

A.Maximize?(枚举)

题意:

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

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

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

分析:

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

此外,因为y<xy\lt xy<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-ygcd(x,xy)xy,所以gcd⁡(x,x−y)+y≤x\gcd(x,x-y)+y\le xgcd(x,xy)+yx,又因为相邻两个数的gcd⁡\gcdgcd111,所以当y=x−1y=x-1y=x1时一定满足题目,可以直接输出x−1x-1x1

代码:

#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(双指针)

题意:

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

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

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

分析:

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

考虑双指针,设置lll指针指向aaa字符串的第一个字母 rrr指针指向bbb字符串的第一个字母,若lllrrr指向的字母相同,则双指针往后移动,并更新ansansans值为lll指针 若lllrrr不相同 则移动rrr指针至第一个可以和lll指针匹配的位置,逐个移动直到lll或者rrr指针跑到nnnmmm

代码:

#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(构造)

题意:

给你一个数组x2,x3,…,xnx_2,x_3,\dots,x_nx2,x3,,xn。你的任务是找出任意一个数组a1,…,ana_1,\dots,a_na1,,an,其中:

  • 对于所有的1≤i≤n1\le i\le n1in1≤ai≤1091\le a_i\le 10^91ai109
  • 对于所有2≤i≤n2\le i\le n2inxi=ai mod ai−1x_i=a_i\bmod a_{i-1}xi=aimodai1

这里的c mod dc\bmod dcmodd表示整数ccc除以整数ddd的余数。例如,5 mod 2=15\bmod 2=15mod2=172 mod 3=072\bmod 3=072mod3=0143 mod 14=3143\bmod 14=3143mod14=3

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

分析:

题目要求构造aia_iai,考虑如果ai−1a_{i-1}ai1aia_iai大的话,根据模的计算方法,可以直接放入xix_ixi

我们每次构造出的aia_iai,都让它等于ai−1+xia_{i-1}+x_iai1+xi,让数组aaa一直递增,即可符合要求。

代码:

#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发现了一个排列p1,…,pnp_1,\dots,p_np1,,pn和一个数组a1,…,ana_1,\dots,a_na1,,an。他们决定玩一个著名的 “排列游戏”。

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

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

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

  • 如果棋手当前的位置是xxx,他的得分就会增加axa_xax
  • 然后棋手要么停留在当前位置xxx,要么xxx移动到pxp_xpx

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

知道了Bodya的起始位置PBP_BPB和Sasha的起始位置PSP_SPS后,如果两位棋手都想获胜,那么谁会赢得对局?

分析:

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

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

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

代码:

#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(构造)

题意:

给你一个整数nnn。您在网格n×nn\times nn×n中选择nnn个单元格(x1,y1),(x2,y2),…,(xn,yn)(x_1,y_1),(x_2,y_2),\dots,(x_n,y_n)(x1,y1),(x2,y2),,(xn,yn),其中1≤xi≤n1\le x_i\le n1xin1≤yi≤n1\le y_i\le n1yin

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

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

单元格(x1,y1)(x_1,y_1)(x1,y1)(x2,y2)(x_2,y_2)(x2,y2)之间的曼哈顿距离等于∣x1−x2∣+∣y1−y2∣|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>1k\gt 1k>1部分,使得每一部分值按位异或都相等,那么我们就称这个数组为x1,…,xmx_1,\dots,x_mx1,,xm有趣。

更正式地说,你必须把数组xxx分成kkk个连续的部分,xxx中的每个元素都必须准确地属于111部分。设y1,…,yky_1,\dots,y_ky1,,yk分别是各部分元素的XOR。那么y1=y2=⋯=yky_1=y_2=\dots=y_ky1=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}01=1=230

给你一个数组a1,…,ana_1,\dots,a_na1,,an。你的任务是回答qqq次查询:

  • 对于固定的lll,rrr, 判断子数组al,al+1,…,ara_l,a_{l+1},\dots,a_ral,al+1,,ar是否有趣。

分析:

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

  1. Sr⊕Sl−1=0S_r\oplus S_{l-1}=0SrSl1=0

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

  1. Sr⊕Sl−1=vS_r\oplus S_{l-1}=vSrSl1=v

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

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

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

找到最大的i<ri\lt ri<r使得Sr⊕Si=vS_r\oplus S_i=vSrSi=v,如果找不到则无解。

找到最大的j<ij\lt ij<i使得Si⊕Sj=vS_i\oplus S_j=vSiSj=v,如果找不到或则j<l−1j\lt l-1j<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=rl=rl=r

给你一个字符串sss。对于固定的kkk,考虑将sss恰好分成kkk个连续的子串w1,…,wkw_1,\dots,w_kw1,,wk。假设fkf_kfk是所有分割中最大的LCP(w1,…,wk)LCP(w_1,\dots,w_k)LCP(w1,,wk)

LCP(w1,…,wm)LCP(w_1,\dots,w_m)LCP(w1,,wm)是字符串w1,…,wmw_1,\dots,w_mw1,,wm的最长公共前缀的长度。

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

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

分析:

如果存在划分使得LCPLCPLCPvvv,则任意v‘<vv \text{`} \lt vv<v都存在划分。

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

令前缀字符串为tttsss种最多有xxx个不相交的子串ttt

如果x≥kx\ge kxk,则存在划分使得LCPLCPLCPttt

可以用kmpkmpkmp或字符串哈希求出。

代码:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值