2020牛客暑期多校训练营(第三场)A、B、C、D、E、F、G、L题解及补题

2020牛客暑期多校训练营(第三场)题解及补题

比赛过程

首先L是签到,然后过了AB两签到,然后按顺序写了C、F、E三题。总的来说,前期写AB的时候,队内的交流不够充分,因为本来以为都是签到自顾自写没什么问题,结果白白wa了很多发,以后的比赛中除了L这种签到程度的签到,我们还是应该通过交流来保证思路和算法的正确性。然后中后期过的三个题,队的交流还是比较充分的,相比前几场有了明显提升。可惜的是D有了完整思路只有一个细节有小错误来不及改了,以后或许可以在比赛时有更优的分配策略。赛后补了D和G。总的来说这场偏思维,我们的排名也比之前要高一点,但是也可以发现我们的弱侧还是在于算法的技能树,需要多学习专项的算法。

题解

A

题意

n个台阶,每个台阶可能有鱼或者蛤蜊共四种组合可能,然后有四种选择,分别是有鱼的情况下直接拿鱼,或者捡起蛤蜊变成鱼饵,或者把鱼饵换成鱼,或者什么都不做。

解法

有鱼拿鱼,没鱼的话唯一决策点在于只有蛤蜊的时候是拿蛤蜊还是鱼饵换鱼。我们可以弄个后缀记录这点后面可以把鱼饵变成鱼的点(即0或1个数和),若当前鱼饵数比可变点少则拿蛤蜊,反之换鱼。

代码
#include <stdio.h>

#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const ll maxn = 2e6 + 111;
char s[maxn];
int tail[maxn];
int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        int n;
        scanf("%d", &n);
        scanf("%s", s + 1);
        int ans = 0, fb = 0;
        for (int i = 1; i <= n; ++i) {
            if (s[i] == '3' || s[i] == '2') {
                ++ans;
            }
        }
        tail[n + 1] = 0;
        for (int i = n; i >= 1; --i) {
            if (s[i] == '0' || s[i] == '1') {
                tail[i] = tail[i + 1] + 1;
            } else
                tail[i] = tail[i + 1];
        }
        for (int i = 1; i <= n; ++i) {
            if (s[i] == '1') {
                if (fb < tail[i + 1]) {
                    ++fb;
                } else {
                    if (fb)
                        --fb, ++ans;
                    else
                        ++fb;
                }
            } else if (s[i] == '0') {
                if (fb) ++ans, --fb;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

B

题意

给了一个字符串和 q q q 次操作,每次操作可以将左边长度为 x x x 的子串移动到最右边或者将右边长度为 x x x 的子串移动到最左边,或者输出 s x s_x sx

解法

把字符串想象成头尾相接的,那么移动的过程就相当于第一个字符发生了变化,所以可以 O ( 1 ) O(1) O(1) 操作每次询问。

代码
#pragma region
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#define debug() printf("     ");
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
template <class T>
void _R(T &x) { cin >> x; }
void _R(int &x) { scanf("%d", &x); }
void _R(int64_t &x) { scanf("%lld", &x); }
void _R(double &x) { scanf("%lf", &x); }
void _R(char &x) { x = getchar(); }
void _R(char *x) { scanf("%s", x); }
void R() {}
template <class T, class... U>
void R(T &head, U &... tail) { _R(head), R(tail...); }
template <class T>
void _W(const T &x) { cout << x; }
void _W(const int &x) { printf("%d", x); }
void _W(const int64_t &x) { printf("%lld", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
template <class T, class U>
void _W(const pair<T, U> &x) { _W(x.F), putchar(' '), _W(x.S); }
template <class T>
void _W(const vector<T> &x) {
    for (auto i = x.begin(); i != x.end(); _W(*i++))
        if (i != x.cbegin()) putchar(' ');
}
void W() {}
template <class T, class... U>
void W(const T &head, const U &... tail) { _W(head), putchar(sizeof...(tail) ? ' ' : '\n'), W(tail...); }
#pragma endregion
int main() {
    IO;
    string s;
    cin >> s;
    int q;
    cin >> q;
    int pos = 0, len = s.length();
    while (q--) {
        char a;
        int x;
        cin >> a >> x;
        if (a == 'A')
            cout << s[(pos + len + x - 1) % len] << endl;
        else {
            pos += x;
            pos = (pos + len) % len;
        }
    }
}

C

题意

给出一个固定大小和形状的左手(右手与之对称)。然后给你一只手的坐标点(可旋转、不可伸缩)。判断是左手还是右手。

解法

容易找到底边A、B,从而找到大拇指的点C(与B相连),BC与BA叉积即可判断他们位置关系。

代码
#pragma region
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#define debug() printf("     ");
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
template <class T>
void _R(T &x) { cin >> x; }
void _R(int &x) { scanf("%d", &x); }
void _R(int64_t &x) { scanf("%lld", &x); }
void _R(double &x) { scanf("%lf", &x); }
void _R(char &x) { x = getchar(); }
void _R(char *x) { scanf("%s", x); }
void R() {}
template <class T, class... U>
void R(T &head, U &... tail) { _R(head), R(tail...); }
template <class T>
void _W(const T &x) { cout << x; }
void _W(const int &x) { printf("%d", x); }
void _W(const int64_t &x) { printf("%lld", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
template <class T, class U>
void _W(const pair<T, U> &x) { _W(x.F), putchar(' '), _W(x.S); }
template <class T>
void _W(const vector<T> &x) {
    for (auto i = x.begin(); i != x.end(); _W(*i++))
        if (i != x.cbegin()) putchar(' ');
}
void W() {}
template <class T, class... U>
void W(const T &head, const U &... tail) { _W(head), putchar(sizeof...(tail) ? ' ' : '\n'), W(tail...); }
#pragma endregion
typedef pair<double, double> pdd;
const double eps = 1e-4;
inline int sgn(const double &x) { return x < -eps ? -1 : x > eps; }
inline bool equa(double A, double B) { return sgn(A - B) == 0; }
inline double dis(pdd A, pdd B) { return sqrt((A.first - B.first) * (A.first - B.first) + (A.second - B.second) * (A.second - B.second)); }
inline double det(pdd A, pdd B) { return A.first * B.second - A.second * B.first; }
int main() {
    int T;
    R(T);
    while (T--) {
        vector<pdd> v;
        int n = 20;
        rep(i, 1, 20) {
            double x, y;
            R(x, y);
            v.push_back({x, y});
        }
        int p1 = -1, p2 = -1, p3 = -1, p4 = -1;  //p2,p3是(1,0)
        rep(i, 0, 19) {
            if (equa(dis(v[i], v[(i + 1) % n]), 6.0)) p1 = i, p2 = (i + 1) % n;
            if (equa(dis(v[i], v[(i + 1) % n]), 9.0)) p3 = i, p4 = (i + 1) % n;
        }
        // W(p1, p2, p3, p4);
        // W(dis(v[p1], v[p2]));
        // W(dis(v[p3], v[p4]));
        if (p1 == p3) swap(p1, p2);
        if (p1 == p4) swap(p1, p2), swap(p3, p4);
        if (p2 == p4) swap(p3, p4);
        if (det({v[p1].first - v[p2].first, v[p1].second - v[p2].second}, {v[p4].first - v[p3].first, v[p4].second - v[p3].second}) > 0)
            W("left");
        else
            W("right");
    }
}

D

题意

无限大格点平面,问能否通过涂黑n个点来得到m个相邻异色点对。

解法

假设连续的一片点的个数为n,预处理找到每个n对应的最少点数和最多点数,可以发现以2为单位变换,所以m必须是偶数。再者对于给出的n,假设连续的点的个数为i,去遍历i寻找答案即可。比赛时对于最少点数的求法出了错,应该是对于ab>=n的a、b 去寻找2(a+b),而不是a*b==n的a、b。

代码
#include <stdio.h>

#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
const ll maxn = 2e5 + 111;
ll num[100];
ll mp[1000][1000] = {0};
pair<ll, ll> pp[100];
int main() {
    num[1] = 4;
    pp[1] = {1, 1};
    for (ll i = 2; i <= 50; ++i) {
        ll j;
        for (j = 1; j <= i; ++j) {
            if (j * j >= i) {
                break;
            }
        }
        num[i] = (j + (i + j - 1) / j) * 2;
        pp[i] = {j, (i + j - 1) / j};
    }
    ll T;
    scanf("%lld", &T);
    while (T--) {
        ll n, m;
        scanf("%lld%lld", &n, &m);
        for (int i = 1; i <= 100; ++i) {
            for (int j = 1; j <= 100; ++j) {
                mp[i][j] = 0;
            }
        }
        if ((m & 1) || (m > 4 * n)) {
            puts("No");
        } else {
            ll flag = 0;
            ll depe = 0;
            ll trans = 0;
            ll nnn, mmm;
            for (ll i = 1; i <= n; ++i) {
                // i个连续点
                ll num_d = i;
                ll minn = num[i] + 4 * (n - i), maxx = 2 * i + 2 + 4 * (n - i);
                if (m <= maxx && m >= minn) {
                    flag = 1;
                    depe = n - i;
                    trans = (m - minn) / 2;
                    nnn = pp[i].first, mmm = pp[i].second;
                    for (int ii = pp[i].first; ii >= 1; --ii) {
                        for (int jj = pp[i].second; jj >= 1; --jj) {
                            mp[ii][jj] = 1;
                            --num_d;
                            if (!num_d) break;
                        }
                        if (!num_d) break;
                    }
                    break;
                }
            }
            if (!flag) {
                puts("No");
            } else {
                puts("Yes");
                for (int i = 1, num = 1; num <= depe; ++num, i += 5) {
                    printf("%d %d\n", i, 10000000);
                }
                int ni = 1, nj = 1;
                while (mp[ni][nj] == 0) {
                    ++nj;
                    if (nj == mmm) {
                        nj = 1;
                        ++ni;
                    }
                }
                int toi = nnn, toj = mmm + 1;
                while (trans--) {
                    mp[ni][nj] = 0;
                    ++nj;
                    if (nj == mmm) {
                        nj = 1;
                        ++ni;
                    }
                    mp[toi][toj++] = 1;
                }
                for (int i = 1; i <= 100; ++i) {
                    for (int j = 1; j <= 100; ++j) {
                        if (mp[i][j]) {
                            printf("%d %d\n", i, j);
                        }
                    }
                }
            }
        }
    }
    return 0;
}

E

题意

题面很绕,本质上就是对于数组a,去找两个完全不同的匹配,使得匹配之间差值和最小。

解法

可以发现,4个点或者6个点之中,最优的策略下的答案就是最后一个数减第一个数。由此我们以4or6为一个单位进行dp即可。在每个偶数点位更新答案。

代码
#include <stdio.h>

#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
const ll maxn = 2e5 + 111;
ll a[maxn];
ll dp[maxn];
int main() {
    ll T;
    scanf("%lld", &T);
    while (T--) {
        ll n;
        scanf("%lld", &n);
        for (ll i = 1; i <= n; ++i) {
            scanf("%lld", a + i);
        }
        sort(a + 1, a + 1 + n);
        ll ans = 0;
        dp[4] = (a[4] - a[1]) * 2;
        dp[6] = (a[6] - a[1]) * 2;
        dp[8] = dp[4] + (a[8] - a[5]) * 2;
        for (ll i = 10; i <= n; i += 2) {
            dp[i] = min(dp[i - 4] + (a[i] - a[i - 3]) * 2,
                        dp[i - 6] + (a[i] - a[i - 5]) * 2);
        }
        printf("%lld\n", dp[n]);
    }
    return 0;
}

F

题意

给出a与b找到c、d、e、f使得 c d − e f = a b \frac{c}{d}-\frac{e}{f}=\frac{a}{b} dcfe=ba且$ d< b $ and $ f < b、1<=c,e<=4e12 $。

解法

首先b为1则直接-1,然后a、b不互质易得答案,然后若b为质数或者只有一个质因子,显然也是-1,最后b不为质数则可以拆成俩互质的数n、m,从而解cm-en=a即可,直接exgcd。

代码
#pragma region
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#define debug() printf("     ");
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
template <class T>
void _R(T &x) { cin >> x; }
void _R(int &x) { scanf("%d", &x); }
void _R(int64_t &x) { scanf("%lld", &x); }
void _R(double &x) { scanf("%lf", &x); }
void _R(char &x) { x = getchar(); }
void _R(char *x) { scanf("%s", x); }
void R() {}
template <class T, class... U>
void R(T &head, U &... tail) { _R(head), R(tail...); }
template <class T>
void _W(const T &x) { cout << x; }
void _W(const int &x) { printf("%d", x); }
void _W(const int64_t &x) { printf("%lld", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
template <class T, class U>
void _W(const pair<T, U> &x) { _W(x.F), putchar(' '), _W(x.S); }
template <class T>
void _W(const vector<T> &x) {
    for (auto i = x.begin(); i != x.end(); _W(*i++))
        if (i != x.cbegin()) putchar(' ');
}
void W() {}
template <class T, class... U>
void W(const T &head, const U &... tail) { _W(head), putchar(sizeof...(tail) ? ' ' : '\n'), W(tail...); }
#pragma endregion
const int maxn = 2e6 + 5;
ll mp[maxn];
// vector<int> prime;
void find() {
    for (ll i = 2; i < maxn; ++i) {
        if (mp[i]) continue;
        // prime.push_back(i);
        for (ll j = i * i; j < maxn; j += i)
            mp[j] = i;
    }
}
ll exgcd(ll a, ll b, ll &x, ll &y) {
    if (!b) {
        x = 1;
        y = 0;
        return a;
    }
    ll d = exgcd(b, a % b, x, y);
    ll t = x;
    x = y;
    y = t - (a / b) * y;
    return d;
}
pair<ll, ll> cal(ll n, ll m, ll a) {
    ll x, y;
    ll dd = exgcd(n, m, x, y);
    ll d1 = n / dd;
    ll d2 = m / dd;
    if (x < 0) {
        ll cnt = x / d2 + 1;
        x += cnt * d2, y -= cnt * d1;
    }
    if (y > 0) {
        ll cnt = y / d1 + 1;
        x += cnt * d2, y -= cnt * d1;
    }
    return {a * x, -a * y};
}

int main() {
    int T;
    R(T);
    find();
    while (T--) {
        ll a, b;
        R(a, b);
        ll g = __gcd(a, b);
        if (b == 1) {
            W("-1 -1 -1 -1");
        } else if (g != 1) {
            a /= g, b /= g;
            W(a + 1, b, 1, b);
        } else if (!mp[b]) {  //b是质数
            W("-1 -1 -1 -1");
        } else {
            ll b1 = mp[b], b2 = 1;
            while (b % b1 == 0) b /= b1, b2 *= b1;
            if (b == 1) {
                W("-1 -1 -1 -1");
            } else {
                pair<ll, ll> ans = cal(b2, b, a);
                W(ans.first, b, ans.second, b2);
            }
        }
    }
}

G

题意

给你 n n n 个点, m m m 条边,一开始,所有点自身是一个group,一共 q q q 次操作,每次操作将所有与这个group中的点相连的点合并成一个group,求所有操作后每个点所在的group。

解法

并查集维护点的group信息,每次将与u相连的点v合并的时候,就把这个点相连的所有信息合并到u中即可。

代码
#pragma region
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#define debug() printf("     ");
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
template <class T>
void _R(T &x) { cin >> x; }
void _R(int &x) { scanf("%d", &x); }
void _R(int64_t &x) { scanf("%lld", &x); }
void _R(double &x) { scanf("%lf", &x); }
void _R(char &x) { x = getchar(); }
void _R(char *x) { scanf("%s", x); }
void R() {}
template <class T, class... U>
void R(T &head, U &... tail) { _R(head), R(tail...); }
template <class T>
void _W(const T &x) { cout << x; }
void _W(const int &x) { printf("%d", x); }
void _W(const int64_t &x) { printf("%lld", x); }
void _W(const double &x) { printf("%.16f ", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
template <class T, class U>
void _W(const pair<T, U> &x) { _W(x.F), putchar(' '), _W(x.S); }
template <class T>
void _W(const vector<T> &x) {
    for (auto i = x.begin(); i != x.end(); _W(*i++))
        if (i != x.cbegin()) putchar(' ');
}
void W() {}
template <class T, class... U>
void W(const T &head, const U &... tail) { _W(head), putchar(sizeof...(tail) ? ' ' : '\n'), W(tail...); }
#pragma endregion
const int maxn = 8e5 + 5;
vector<int> g[maxn];
int pre[maxn], n, m;
int findroot(int x) { return pre[x] == x ? x : pre[x] = findroot(pre[x]); }
void link(int x, int y) { pre[findroot(x)] = pre[findroot(y)]; }
void init() {
    rep(i, 0, n - 1) {
        pre[i] = i;
        g[i].clear();
    }
}
int main() {
    int T;
    R(T);
    while (T--) {
        R(n, m);
        init();
        while (m--) {
            int u, v;
            R(u, v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        int q;
        R(q);
        while (q--) {
            int x;
            R(x);
            if (x != pre[x]) continue;
            vector<int> t;
            for (auto u : g[x]) {
                int uu = findroot(u);
                if (x != uu) {
                    if (t.size() < g[uu].size()) swap(g[uu], t);
                    for (auto v : g[uu]) t.push_back(v);
                    g[uu].clear();
                    link(uu, x);
                }
            }
            swap(t, g[x]);
        }
        rep(i, 0, n - 1) printf("%d%c", findroot(i), " \n"[i == n - 1]);
    }
}

H

题意
解法
代码
//将内容替换成代码

I

题意
解法
代码
//将内容替换成代码

J

题意

判断串是否是lovely开头,对大小写不区分。

解法

签到,直接判断。

代码
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")

#include <bits/stdc++.h>
//#include <ext/pb_ds/assoc_container.hpp>
//#include <ext/pb_ds/tree_policy.hpp>
//#include <ext/pb_ds/priority_queue.hpp>

//using namespace __gnu_pbds;
using namespace std;

#define ll long long
#define ld long double
#define ull unsigned long long
#define mst(a, b) memset((a), (b), sizeof(a))
#define mp(a, b) make_pair(a, b)
#define pi acos(-1)
#define endl '\n'
#define pii pair<int, int>
#define pll pair<ll, ll>
#define pdd pair<double, double>
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#define lowbit(x) x &(-x)
#define all(x) (x).begin(), (x).end()
#define sf(x) scanf("%d", &x)
#define pf(x) printf("%d", x)
#define debug(x) cout << x << endl
#define mod(x) (x % mod + mod) % mod

template <typename T>
void read(T &x)
{
    x = 0;
    char ch = getchar();
    ll f = 1;
    while (!isdigit(ch))
    {
        if (ch == '-')
            f *= -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = x * 10 + ch - 48;
        ch = getchar();
    }
    x *= f;
}

const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-8;
const int maxn = 1e5 + 7;
const int maxm = 1e5 + 7;
const int mod = 1e9 + 7;

#define IO ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
//template<typename T> using ordered_set = tree<T,null_type,less<T>,rb_tree_tag,tree_order_statistics_node_update>;

int main()
{
    IO;
    string s;
    cin>>s;
    if ((s[0]=='l'||s[0]=='L')&&(s[1]=='o'||s[1]=='O')&&(s[2]=='v'||s[2]=='V')&&(s[3]=='e'||s[3]=='E')&&(s[4]=='l'||s[4]=='L')&&(s[5]=='y'||s[5]=='Y'))
    cout<<"lovely\n";
    else
    cout<<"ugly\n";
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值