暑假集训_复健杂题

A   Getting Zero

CodeForces 1661B

题意

给一个数字 a ,可以进行如下两个操作。

 a=(a+1 )%32768

 a=(a*2 )%32768

问最多进行几次操作可以使a=0.

思路

易得   ,因此对于一个数最多进行15次操作,可以暴力求解,为了使一个数尽量变大因先加在乘。

代码

#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long ll;
const ll N = 1e4 + 233;
int pj[20];
const int mod = 32768;
void solve()
{
    int x;
    cin >> x;
    int cnt = 15;
    for (int i = 0; i <= 15; i++)
        for (int j = 0; j <= 15; j++)
            if ((x + i) * pj[j] % mod == 0)
                cnt = min(cnt, i + j);
    cout << cnt << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    pj[0] = 1;
    for (int i = 1; i <= 15; i++)
        pj[i] = pj[i - 1] * 2;
    int t;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

 

B Increase Subarray Sums

CodeForces 1644C

题意

给定一个序列 {与整数 x

定义 f(k) 表示经过如下操作后, 序列 a 中最大的连续子段和: 将 a 中 k 个不同的位置上的数加上 x

请求出 f(k), k[0,n]

思路

先求出区间长度为分别为0~n时的最大的连续子段和,再加上k个x时枚举求出最大数。

代码

#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long ll;
const ll N = 1e9;
int s[5010], f[5010];
void solve()
{
    int n, x;
    cin >> n >> x;
    for (int i = 1; i <= n; i++)
    {
        int a;
        cin >> a;
        s[i] = s[i - 1] + a;
    }
    for (int i = 0; i <= n; i++)
        f[i] = -N;
    f[0]=0;
    for (int i = 1; i <= n; i++)
        for (int j = i; j <= n; j++)
            f[i] = max(f[i], s[j] - s[j - i]);
    for (int i = 0; i <= n; i++)
    {
        int ans = -N;
        for (int j = 0; j <= n; j++)
            ans = max(ans, f[j] + min(i, j) * x);
        cout << ans << " ";
    }
    cout << endl;
}

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

    return 0;
}

 

 

C Integral Array

CodeForces 1648B

题意

给定一个数组 a , 我们称该数组完整需要满足 :若数组a 中存在两数  x,y  , 使 yx  (x,y  可以是同一个数) , 则x/y⌋也必须在数组 a 中 , 现需要判断数组a 是否完整 。

思路

这样,我们先找到一个在数组出现过的 i,再找到一个没有在数组里出现过的 j,因为 j 不存在,所以 i*j 是一定不会出现在一个完整的数组里的。如果i*j出现在数组里了,那么说明这个数组一定不是完整的。

首先开一个 cnt[] 数组记录每个数出现的个数,然后对 cnt 数组进行前缀和(前缀和在判断数是否出现时有妙用)。

然后双重循环开始枚举i和j,需要判断cnt[i] > 0 和 cnt[j] == 0,如果前缀和 sum_cnt[i * j - 1] !=sum_cnt[i * (j + 1) - 1] 则说明在 i * j ~ i * (j + 1) - 1 之间有数存在,说明这个数组一定不是好的。

解释一下这里前缀和的使用:

如果sum[1] == sum[10] 说明在[1,10]区间内没有数,两者才会相等

代码

 

#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long ll;
const ll N = 1e5, mod = 1e9 + 7;
ll all[N];
ll sum_cnt[N]; // 前缀和cnt
ll cnt[N];

void solve()
{
    int n, c;
    cin >> n >> c;
    for (int i = 0; i <= 2 * c; i++)
        cnt[i] = 0; // 初始化cnt
    for (int i = 1; i <= n; i++)
        cin >> all[i], cnt[all[i]]++;
    for (int i = 1; i <= 2 * c; i++)
        sum_cnt[i] = sum_cnt[i - 1] + cnt[i];
    ll x = 0, ok = 1;
    for (int i = 1; i <= c; i++)
    {
        if (cnt[i] > 0)
        {
            for (int j = 1; i * j <= c; j++)
            {
                if (cnt[j] == 0)
                {
                    if (sum_cnt[i * j - 1] != sum_cnt[i * (j + 1) - 1])
                    {
                        ok = 0;
                        break;
                    }
                }
            }
        }
    }
    if (ok)
        cout << "Yes" << endl;
    else
        cout << "No" << endl;
}

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

 

 

D Water the Trees

CodeForces 1661C

题意

有一个长 n 的数列 h1,h2,,hn,对于每次操作,你有两种方法:

  • 选择一个数,如果这是第奇数次操作,给它加 1,否则,给它加 2。
  • 什么也不做(但也算一次操作)。

注:每次操作最多只能选一个数。

求至少要经过多少次操作,使得数列中所有数均相等。

 思路

我们发现,只有两种情况,奇数次和偶数次。那么,可以贪心来考虑。

可以发现,设定一个目标高度,显然每棵树有两种情况,要么差是偶数,要么差是奇数。

这时,偶数我们就可以在偶数次操作,奇数可以在奇数次操作。但最后肯定会剩余某些树,要么是全差奇数次,要么是全差偶数次。

所以说,如果需要 +2 的次数多,就尽可能把 +2 分给 +1。

我们可以 3个 +2 为一组,为什么呢?可以发现,等于 2 个 +1 和 2 个 +2,这时再去贪心求解,答案就出来了。

代码

#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long ll;
const ll N = 1e6;
ll s[N], n;
ll solve(int x)
{
    ll a = 0, b = 0;
    ll ans = 0;
    for (int i = 1; i <= n; i++)
    {
        a += (x - s[i]) % 2;
        b += (x - s[i]) / 2;
    }
    if (b - a > 1)
    {
        ll x = b - a + 1;
        a += (x / 3) * 2;
        b -= x / 3;
    }
    if (a > b)
        return a * 2 - 1;
    else
        return b * 2;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--)
    {
        cin >> n;
        ll x = -N;
        for (int i = 1; i <= n; i++)
        {
            cin >> s[i];
            x = max(x, s[i]);
        }
        cout << min(solve(x), solve(x + 1)) << endl;
    }
    return 0;
}

 

 

E Make it Increasing

CodeForces 1667A

题意

 给定一个包含 n 个正整数的数列 a 以及一个长度为 n 的数列 b ,初始时数列 b 的每一个元素都为0。

定义一次操作为把数列 b 中的某个元素  b加上或减去 ai 的值,求使得数列 b 严格递增最小的操作次数。

思路

数据范围比较小可以暴力求解,完成操作后的数组 b 可以看成三部分。

负数,0,正数。

枚举 0 的位置,向前和向后遍历,将 ai 乘上一个数使它刚好比上一个 bi+1 小(或者比 bi-1 大)。

最后输出操作数量的最小值即可。

代码

#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long ll;
const ll N = 1e18;
void solve()
{
    int n;
    cin >> n;
    ll s[n + 10];
    ll ans = N;
    for (int i = 1; i <= n; i++)
        cin >> s[i];
    for (int i = 2; i < n; i++)
    {
        ll a = 0, b = 0;
        ll cnt = 0;
        for (int j = i - 1; j >= 1; j--)
        {
            if (s[j] > cnt)
            {
                a++;
                cnt = s[j];
            }
            else
            {
                ll k = cnt / s[j] + 1;
                cnt = s[j] * k;
                a = a + k;
            }
        }
        cnt = 0;
        for (int j = i + 1; j <= n; j++)
        {
            if (s[j] > cnt)
            {
                b++;
                cnt = s[j];
            }
            else
            {
                ll k = cnt / s[j] + 1;
                cnt = s[j] * k;
                b = b + k;
            }
        }
        ans = min(ans, a + b);
    }
    cout << ans;
}

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

    return 0;
}

 

posted @ 2022-07-06 15:34  黎_lyh  阅读(28)  评论(0)    收藏  举报