2022牛客寒假算法基础集训营1

在2023年牛客寒假算法基础集训营开始前,刚好没什么事,就来补一下2022年的寒假集训题目。

L题:

牛牛学走路

做法:正常模拟一下就行。

代码如下:

/*
coder:sunshine
school:njupt
*/
#include 
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;

void solve()
{
    int n;
    string s;
    cin >> n >> s;
    int a = 0, b = 0;
    double ans = 1.0;
    for (auto c : s)
    {
        if (c == 'L')
        {
            a--;
            ans = max(ans, sqrt(a * a + b * b));
        }
        else if (c == 'R')
        {
            a++;
            ans = max(ans, sqrt(a * a + b * b));
        }
        else if (c == 'U')
        {
            b++;
            ans = max(ans, sqrt(a * a + b * b));
        }
        else
        {
            b--;
            ans = max(ans, sqrt(a * a + b * b));
        }
    }
    printf("%.12f\n", ans);
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

E题:

炸鸡块君的高中回忆

做法:不能暴力模拟,不然时间复杂度过高就超时了,要推公式,注意 n = m = 1 n=m=1 n=m=1时的特判

代码如下:

/*
coder:sunshine
school:njupt
*/
#include 
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;

void solve()
{
    int n, m;
    cin >> n >> m;
    if (m == 1 && n != 1)
    {
        cout << -1 << endl;
        return;
    }
    if (m == n)
    {
        cout << 1 << endl;
        return;
    }
    cout << ((n - 1) / (m - 1) + ((n - 1) % (m - 1) != 0)) * 2 - 1 << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

J题:

题目大意:有a,b两个数组,从中选n个数组成一圈,要确保选的b数组中的数不能两两相连,求最大值,不行就输出-1。

做法:用贪心加前缀和,可以先给a,b数组从大到小排个序,然后算一下前缀和。接着遍历a数组可选用数的个数,把不符合的跳过,符合的每种情况算一下取个max。

代码如下:

/*
coder:sunshine
school:njupt
*/
#include 
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;

const int N = 1e4 + 10;
int a[N], b[N];
int sum1[N], sum2[N];

void solve()
{
    int A, B, n;
    cin >> A >> B >> n;
    for (int i = 1; i <= A; i++)
    {
        cin >> a[i];
    }
    for (int i = 1; i <= B; i++)
    {
        cin >> b[i];
    }
    sort(a + 1, a + A + 1, [&](int c, int d)
         { return c > d; });
    sort(b + 1, b + B + 1, [&](int c, int d)
         { return c > d; });
    for (int i = 1; i <= A; i++)
    {
        sum1[i] = sum1[i - 1] + a[i];
    }
    for (int i = 1; i <= B; i++)
    {
        sum2[i] = sum2[i - 1] + b[i];
    }
    int maxb = min(n / 2, B);
    if (A + maxb < n)
    {
        cout << -1 << endl;
        return;
    }
    int ans = -1;
    for (int i = 0; i <= A; i++)
    {
        int j = n - i;
        if (j > maxb || j < 0)
            continue;
        ans = max(sum1[i] + sum2[j], ans);
    }
    cout << ans << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

A题:

九小时九个人九扇门

题目大意:给定n个数,求出在n个数中选出若干数的和的数字根为 1   9 1~9 1 9的个数。

做法:首先我们要知道数字根有一个性质:一个数的数字根就是这个数对9取模的结果,特别的,数字根为9取模为0。那么这道题就可以转换成一道01背包变形的问题。 f [ i ] [ j ] f[i][j] f[i][j]即是前i个数求和的数字根为j的结果。时间复杂度为 O ( 9 ∗ n ) O(9*n) O(9n)

代码如下:

/*
coder:sunshine
school:njupt
*/
#include 
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 998244353;
const int N = 1e5 + 10;
int f[N][10];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    f[0][0] = 1;
    for (int i = 1; i <= n; i++)
    {
        int x;
        cin >> x;
        x %= 9;
        for (int j = 0; j < 9; j++)
        {
            f[i][(j + x) % 9] = (f[i][(j + x) % 9] + f[i - 1][j]) % mod;
            f[i][j] = (f[i][j] + f[i - 1][j]) % mod;
        }
    }
    for (int i = 1; i < 9; i++)
    {
        cout << f[n][i] << " ";
    }
    cout << f[n][0] - 1 << endl;
    return 0;
}

H题:

牛牛看云

题目大意:给定 n ( n ≤ 1 e 6 ) n(n\leq1e6) n(n1e6)个数,每个数 ≤ 1000 \leq1000 1000,求下面这个式子:
∑ i = 1 n ∑ j = i n ∣ a i + a j − 1000 ∣ \sum_{i=1}^{n} \sum_{j=i}^{n} |a_i+a_j-1000| i=1nj=inai+aj1000∣
做法:暴力循环n肯定不行,要 t l e tle tle了,所以换个角度,因为每个数 ≤ 1000 \leq1000 1000,所以遍历 0 − 1000 0-1000 01000,在输入的时候统计每个数出现的次数,枚举数的时候,不同的数对 ( i , j ) (i,j) (i,j)直接相乘,相同的数对 ( i , j ) (i,j) (i,j)特殊算,为 c n t [ i ] + C c n t [ i ] 2 cnt[i]+C^{2}_{cnt[i]} cnt[i]+Ccnt[i]2,在乘以 ∣ a i + a j − 1000 ∣ |a_i+a_j-1000| ai+aj1000∣

代码如下:

/*
coder:sunshine
school:njupt
*/
#include 
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;

const int N = 1010;
ll cnt[N];

ll C(ll cnt)
{
    return cnt * (cnt - 1) / 2ll;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        int x;
        cin >> x;
        cnt[x]++;
    }
    ll ans = 0;
    for (int i = 0; i <= 1000; i++)
    {
        for (int j = i; j <= 1000; j++)
        {
            ll add;
            if (i == j)
                add = C(cnt[i]) + cnt[i];
            else
                add = cnt[i] * cnt[j];
            ans += add * abs(i + j - 1000);
        }
    }
    cout << ans << endl;
    return 0;
}

C题:

Baby’s first attempt on CPU

题目大意:一读一写两个语句中间要隔三个及以上的语句,问满足条件后要加几局空语句。

做法:维护一下每个语句的位置,并且每次要更新下位置,如果是一,位置不满足的话,就添加句子,再更新位置。

代码如下:

/*
coder:sunshine
school:njupt
*/
#include 
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;

const int N = 105;
array<int, 5> a[N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i][1] >> a[i][2] >> a[i][3];
        a[n][4] = i;
    }
    int ans = 0;

    for (int i = 1; i <= n; i++)
    {
        a[i][4] = a[i - 1][4] + 1;
        for (int j = 1; j <= 3; j++)
        {
            if (a[i][j] == 1)
            {
                if (a[i][4] - a[i - j][4] < 4)
                {
                    ans += 4 - (a[i][4] - a[i - j][4]);
                    a[i][4] += 4 - (a[i][4] - a[i - j][4]);
                }
            }
        }
    }
    cout << ans << endl;
    return 0;
}

F题:

中位数切分

题目大意:给n个数和m,求把n个数切成若干段,保证每段的中位数都 ≥ m \geq m m,问能切成几段,若不能切,则输出-1。

做法: O ( n ) O(n) O(n)的做法,统计 ≥ m \geq m m的个数 c n t 1 cnt1 cnt1 < m <m的个数 c n t 2 cnt2 cnt2,答案即是 c n t 1 − c n t 2 cnt1-cnt2 cnt1cnt2,如果答案 ≤ 0 \leq 0 0,则输出-1。

代码如下:

/*
coder:sunshine
school:njupt
*/
#include 
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;

const int N = 1e5 + 10;
int a[N];
void solve()
{
    int n, m;
    cin >> n >> m;
    int cnt1 = 0, cnt2 = 0;
    for (int i = 1; i <= n; i++)
    {
        int x;
        cin >> x;
        if (x >= m)
            cnt1++;
        else
            cnt2++;
    }
    int ans = cnt1 - cnt2;
    if (ans <= 0)
        cout << -1 << endl;
    else
        cout << ans << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

先写这么多,后面再补。

B题:

B站与各唱各的

题目大意:n个人,m句话,每个人可以选择唱或者不唱,求唱成功的句子的期望。

做法:对于单个句子分析,没唱成功的概率为 p n + ( 1 − p ) n p^n+(1-p)^n pn+(1p)n,则成功的概率为 1 − ( p n + ( 1 − p ) n 1-(p^n+(1-p)^n 1(pn+(1p)n,要使这个式子值最大,通过肉眼看感觉或者求导做,可以得出当 p = 1 2 p=\frac 12 p=21时最大,所以再乘个m即可,注意要乘逆元。

代码如下:

/*
coder:sunshine
school:njupt
*/
#include 
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int p = 1e9 + 7;

int qmi(int m, int k)
{
    int res = 1 % p, t = m;
    while (k)
    {
        if (k & 1)
            res = (ll)res * t % p;
        t = (ll)t * t % p;
        k >>= 1;
    }
    return res;
}

int inv(int x)
{
    return qmi(x, p - 2);
}

void solve()
{
    int n, m;
    cin >> n >> m;
    cout << ((1ll * qmi(2, n) - 2) * inv(qmi(2, n)) % p * m % p) << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

D题:

牛牛做数论

题目大意:给一个n,求 [ 2 , n ] [2,n] [2,n]中使 H ( x ) H(x) H(x)值最大和最小的数, H ( x ) = ϕ ( x ) x H(x)=\frac {\phi(x)}{x} H(x)=xϕ(x)。若n=1,则输出-1。

做法:数论题,求最小值即是求质数前缀积 ≤ n \leq n n中最大的一个。求最大值就是 ≤ n \leq n n的最大的质数。所以可以先打表前缀积预处理下。

代码如下:

/*
coder:sunshine
school:njupt
*/
#include 
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;

int a[9] = {2, 6, 30, 210, 2310, 30030, 510510, 9699690, 223092870};
bool isprime(int x)
{
    if (x < 2)
        return false;
    for (int i = 2; i <= x / i; i++)
        if (x % i == 0)
            return false;
    return true;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--)
    {
        int n;
        cin >> n;
        if (n == 1)
        {
            cout << -1 << endl;
        }
        else
        {
            for (int i = 0; i < 9; i++)
            {
                if (i == 8)
                {
                    cout << a[i] << " ";
                }
                else
                {
                    if (a[i] <= n && a[i + 1] > n)
                    {
                        cout << a[i] << " ";
                        break;
                    }
                }
            }
            while (!isprime(n))
            {
                n--;
            }
            cout << n << endl;
        }
    }
    return 0;
}

你可能感兴趣的:(牛客比赛,算法,贪心算法,c++)