Codeforces Round #702 (Div. 3)解题报告

Codeforces Round #702 (Div. 3) 全部题解

读错题意,写了半天真是心态爆炸,总的来看这次题目不难的。

A. Dense Array

http://codeforces.com/contest/1490/problem/A
在这里插入图片描述
解题思路
相邻的数字必然是倘若不满足的话是需要插入数据的,那么我们模拟插入数据即可.
x = m i n ( a i , a i + 1 ) , y = m a x ( a i , a i + 1 ) x = min(a_i, a_{i+1}), y = max(a_i, a_{i+1}) x=min(ai,ai+1),y=max(ai,ai+1)

  • 倘若 2 ∗ x ≤ y 2 * x \leq y 2xy不需要操作
  • 否则,需要插入数据。不断的贪心插入 2 ⋅ x 2\cdot x 2x,直到满足条件。 其实这有一个公式的,推导与公式如下所示。
    在这里插入图片描述
    模拟乘以2
#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
const int N = 1010, INF = 0x3f3f3f3f;
int n, a[N];
PII b[N];
bool st[N];

int cal(int t1, int t2)
{
    int ret = 0;
    while (2 * t1 < t2)
    {
        ret ++;
        t1 *= 2;
    }
    return ret;
}

int main()
{
    int t;  cin >> t;
    while (t -- )
    {
        cin >> n;
        LL x = 0;
        for (int i = 1; i <= n; i ++ )
        {
            scanf("%d", &a[i]);
        }
        double cnt;
        for (int i = 1, t1, t2; i < n; i ++ )
        {
            t1 = min(a[i], a[i + 1]), t2 = max(a[i], a[i + 1]);
            if (t1 * 2 >= t2)   continue;
            else
            {
                x += cal(t1, t2);
            }
        }
        cout << x << endl;
    }
    return 0;
}

直接上公式,没有过,应该是小数精度会被卡

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
const int N = 1010, INF = 0x3f3f3f3f;
int n, a[N];

int main()
{
    int t;  cin >> t;
    while (t -- )
    {
        cin >> n;
        LL x = 0;
        for (int i = 1; i <= n; i ++ )
        {
            scanf("%d", &a[i]);
        }
        double cnt;
        for (int i = 1, t1, t2; i < n; i ++ )
        {
            t1 = min(a[i], a[i + 1]), t2 = max(a[i], a[i + 1]);
            if (t1 * 2 >= t2)   continue;
            else
            {
                x += ceil(log(t2 * 1.0 / t1) / log(2)) - 1;
            }
        }
        cout << x << endl;
    }
    return 0;
}

B. Balanced Remainders

http://codeforces.com/contest/1490/problem/B
在这里插入图片描述
先统计出来 % 3 \%3 %3余数为 0 , 1 , 2 0,1, 2 0,1,2数字的数量 c 0 , c 1 , c 2 c_0, c_1, c_2 c0,c1,c2,根据倘若多,那么一定会送走,倘若少,一定会挪进行这个性质进行模拟,而且对于特定的 c i c_i ci他给别人,或者是别人给他的交易对象是一样的,即操作是固定的。

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
const int N = 100010, INF = 0x3f3f3f3f;
int n, a[N];
int c[5] = {0, 0, 0};

int opt(int idx, int ave)
{
    int idx2 = (idx + 1) % 3, idx0 = (idx - 1 + 3) % 3;
    int need = 0;
    if (c[idx] < ave)	// 过少,需要向前一个索要
    {
        need = ave - c[idx];
        c[idx0] -= need;
        c[idx] += need;
    }
    else		// 太多,需要给下一个
    {
        need = c[idx] - ave;
        c[idx2] += need;
        c[idx] -= need;
    }
    return need;
}

int main()
{
    int t;  cin >> t;
    while (t -- )
    {

        cin >> n;
        int ave = n / 3;
        memset(c, 0, sizeof c);
        for (int i = 1; i <= n; i ++ )
        {
            scanf("%d", &a[i]);
            c[a[i] % 3] ++;
        }

        int res = 0;
        for (int i = 0; i < 3; i ++ )
        {
            res += opt(i, ave);
        }

        printf("%d\n", res);
    }

    return 0;
}

C. Sum of Cubes

http://codeforces.com/contest/1490/problem/C
在这里插入图片描述
直接就是 将 i 3 i^3 i3在map上进行打表就可以了。
很关键的是预处理多处理几个,不然很容易被卡

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N = 100010;
LL n;
LL a[N];
map<LL, bool> m;

int main()
{
    int t;  cin >> t;
    for (LL i = 1; i <= 10010; i ++ )	// 这里仅仅写到 10000就会 WA,主要是怕他多往后走了一步,查询到了0
    {
        a[i] = i * i * i;
        m[a[i]] = true;
    }

    // cout << m[0] << endl;

    while (t -- )
    {
        scanf("%lld", &n);
        bool flag = false;
        LL surp;
        for (int i = 1; !flag && a[i] <= n; i ++ )
        {
            surp = n - a[i];
            if (m[surp] == true)
            {
                flag = true;
            }
        }

        if (flag)   puts("YES");
        else    puts("NO");
    }
    return 0;
}

D. Permutation Transformation

http://codeforces.com/contest/1490/problem/D
在这里插入图片描述
在这里插入图片描述
一个简单的模拟dfs,每次在区间找最大值作为 root 即可

#include <bits/stdc++.h>
using namespace std;

const int N = 110;
int a[N], n, depth[N];

void build(int fa, int l, int r)
{
    if (l >= r) return;


    int u = -1, v = -1;
    // 左子树
    for (int i = l; i < fa; i ++ )
    {
        if (u == -1 || a[u] < a[i])
            u = i;
    }
    depth[u] = depth[fa] + 1;
    build(u, l, fa - 1);

    // 右子树
    for (int i = fa + 1; i <= r; i ++ )
    {
        if (v == -1 || a[v] < a[i])
            v = i;
    }
    depth[v] = depth[fa] + 1;
    build(v, fa + 1, r);
}

int main()
{
    int t;  cin >> t;
    while (t -- )
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i ++ )
            scanf("%d", &a[i]);

        memset(depth, -1, sizeof depth);
        int v;
        for (int i = 1; i <= n; i ++ )
        {
            if (a[i] == n)
            {
                v = i;
                break;
            }
        }

        depth[v] = 0;
        build(v, 1, n);

        cout << depth[1];
        for (int i = 2; i <= n; i ++ )
            printf(" %d", depth[i]);

        cout << endl;
    }
    return 0;
}

E. Accidental Victory

http://codeforces.com/contest/1490/problem/E
在这里插入图片描述
说白了就是看谁可以赢。
首先我们先将他们按照各自的数值排序,得到数组 a 1 , a 2 ⋅ ⋅ ⋅ , a n a_1,a_2\cdot\cdot\cdot,a_n a1,a2,an,对应编号为 i d x 1 , i d x 2 , ⋅ ⋅ ⋅ , i d x n idx_1, idx_2, \cdot\cdot\cdot,idx_n idx1,idx2,,idxn,我们对数组求取前缀和 s u m 1 , s u m 2 , ⋅ ⋅ ⋅ , s u m n sum_1, sum_2,\cdot\cdot\cdot,sum_n sum1,sum2,,sumn
我们从后往前看,

  • n n n个是最大的,肯定有机会成为winner
  • n − 1 n-1 n1倘若可以成为 w i n n e r winner winner,当且仅当他赢过 1   n − 2 1~n-2 1 n2个人,然后和 a n a_n an比较,倘若他成不了 w i n n e r winner winner那么直接算法结束,比他小的人更成为不了 w i n n n e r winnner winnner
  • 不断这样算下去即可
#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<LL, int> PII;
const int N = 200010;
vector<int> res;
LL a[N];
LL b[N];
PII c[N];
int n;

bool cmp(const PII &t1, const PII &t2)
{
    return t1.first < t2.first;
}
int main()
{
    int t;  cin >> t;
    while (t -- )
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i ++ )
        {
            scanf("%lld", &a[i]);
            c[i].first = a[i];
            c[i].second = i;
        }
        sort(c + 1, c + n + 1, cmp);
        for (int i = 1; i <= n; i ++ )
            b[i] = b[i - 1] + c[i].first;

        res.clear();
        res.push_back(c[n].second);
        for (int i = n - 1; i >= 1; i -- )
        {
            if (b[i] >= c[i + 1].first)
                res.push_back(c[i].second);
            else
                break;
        }
        sort(res.begin(), res.end());
        cout << res.size() << endl;
        cout << res[0];
        for (int i = 1; i < res.size(); i ++ )
            printf(" %d", res[i]);
        cout << endl;
    }
    return 0;
}

F. Equalize the Array

http://codeforces.com/contest/1490/problem/F
在这里插入图片描述
具体思路:
先用map将出现次数存储起来,统计出出现次数为 i i i的数字一共有 c n t i cnt_i cnti个,
那么我们首先模拟一下,想让所有数字出现的次数为 i i i或者是 0 0 0,那么我们需要将出现次数小于等于 i i i的数字全部移除,需要的次数为 c n t 1 ⋅ 1 + ⋅ ⋅ ⋅ c n t i − 1 ⋅ ( i − 1 ) cnt_1\cdot1+\cdot\cdot\cdot cnt_{i-1}\cdot{(i-1)} cnt11+cnti1(i1)
需要将出现次数大于 i i i的数字次数缩减为 i i i
需要的操作次数为$$
⟹ ( i + 1 ) ⋅ c n t i + 1 + ( i + 2 ) ⋅ c n t i + 2 + ⋅ ⋅ ⋅ + ( n ) ⋅ c n t n − i ⋅ ( c n t i + 1 + c n t i + 2 + ⋅ ⋅ ⋅ + c n t n ) \Longrightarrow(i+1)\cdot cnt_{i+1} + (i+2)\cdot cnt_{i+2}+\cdot\cdot\cdot+(n)\cdot cnt_n -i\cdot(cnt_{i+1}+cnt_{i+2}+\cdot\cdot\cdot+cnt_n) (i+1)cnti+1+(i+2)cnti+2++(n)cntni(cnti+1+cnti+2++cntn)
因此我们直接预处理出来 c n t i cnt_i cnti的前缀数组和他们的加权成前缀数组即可

#include <bits/stdc++.h>
using namespace std;


typedef pair<int, int > PII;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int N = 200010;
int a[N + 5];
int n;
map<int, int> m;
int cnt[N + 5];
int sum[N + 5];
LL addval[N + 5];

int main()
{
    int t;  cin >> t;
    while (t -- )
    {
        m.clear();
        scanf("%d", &n);
        for (int i = 1; i <= n; i ++ )
        {
            scanf("%lld", &a[i]);
            m[a[i]] ++;
        }
        memset(cnt, 0, sizeof cnt);
        memset(sum, 0, sizeof sum);
        memset(addval, 0, sizeof addval);
        map<int, int>::iterator it;
        for (it = m.begin(); it != m.end(); it ++)  // 出现it->second 次数的数字进行统计
        {
            /// printf("IT: %d, %d\n",  it->first, it->second);
            cnt[it->second] ++;
        }

		// 计算前缀数组和累加和数组
        for (int i = 1; i <= n; i ++ )  // 最多出现 n 次
        {
            sum[i] = sum[i - 1] + cnt[i];
            addval[i] = addval[i - 1] + LL(cnt[i]) * i;
        }
/*
        printf("CNT:\n\t");
        for (int i = 1; i <= n; i ++ )
        {
            printf("%d ", cnt[i]);
        }cout << endl;
*/
        LL res = 1e16;
        LL up, down;
        for (int i = 1; i <= n; i ++ )
        {
            down = addval[i - 1];
            up = (addval[n] - addval[i - 1]) - (sum[n] - sum[i - 1]) * i;
            res = min(res, up + down);
        }
        cout << res << endl;
    }
    return 0;
}

G. Old Floppy Drive

http://codeforces.com/contest/1490/problem/G
在这里插入图片描述
在这里插入图片描述
本题是一个较为隐蔽的二分题目,需要我们贪心预处理出来可以二分的数组。
首先我们先将前缀数组给求出来,
并且贪心得到 cnt不断增加,而且前缀和也增加的数字,将其放入数组v, 具体见代码max_val部分。
那么数组 v 是一个数值(无论是val,还是opt_cnt)都不断增加的数据

  • 倘若 x i x_i xi在 数组 v 最大值的范围之内,那么直接二分找最小的cnt
  • 否则,需要判断一下整个原数组 a 的累加和是否大于零,倘若小于等于0,无解
  • 否则,是可以经过不断的循环找到这个数的,首先我们根据max(v)找到最小的循环次数,然后二分即可。
    本题最坑的点就是 long long 开的地方很多,几乎所有的数据都要开long long
#include <bits/stdc++.h>
using namespace std;


typedef long long LL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int N = 200010;
int n, m;
LL a[N], x[N];
LL sum[N];
class Node
{
public:
    LL val, cnt;
    Node(LL _val = 0, LL _cnt = 0)
    {
        val = _val, cnt = _cnt;
    }
}b[N];

int main()
{
    int t;  cin >> t;
    while (t -- )
    {
        cin >> n >> m;
        LL cur_max = -INF;
        b[1] = Node(-INF, -1);
        int n2 = 1;
        sum[0] = 0LL;
        for (int i = 1; i <= n; i ++ )
        {
            scanf("%lld", &a[i]);
            sum[i] = sum[i - 1] + a[i];
            if (sum[i] > cur_max)
            {
                cur_max = sum[i];
                b[++ n2] = Node(cur_max, i - 1);    // 注意在这里的 - 1
            }
        }
        for (int i = 1; i <= m; i ++ )
            scanf("%lld", &x[i]);

        // 当前的 b 数字 是 1~n val 增加, cnt增加的递增数组,下面我们的 x 仅仅只需要二分即可;
        /// printf("RES:\n\t");
        LL loop = sum[n];
        for (int i = 1; i <= m; i ++ )
        {
            if (b[n2].val >= x[i])  // 可以直接二分找 >= x[i] 的最小值
            {
                int l = 1, r = n2, mid;
                while (l < r)
                {
                    mid = l + r >> 1;
                    if (b[mid].val >= x[i])
                        r = mid;
                    else
                        l = mid + 1;
                }
                printf("%lld ", b[l].cnt);
            }
            else    // 查看他的 loop
            {
                if (loop <= 0)  // 无法达到
                {
                    printf("-1 ");
                }
                else
                {
                    LL loopcnt = (x[i] - b[n2].val) / loop + ((x[i] - b[n2].val) % loop != 0);
                    x[i] -= loopcnt * loop;
                    int l = 1, r = n2, mid;
                    while (l < r)
                    {
                        mid = l + r >> 1;
                        if (b[mid].val >= x[i])
                            r = mid;
                        else
                            l = mid + 1;
                    }
                    printf("%lld ", b[l].cnt + loopcnt * n);  // loopcnt 是乘以 n 的
                }
            }
        }
        puts("");

    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值