【CF】Day110——杂题 (数学 + 构造 + 二进制 | 贪心 | 递归 + 模拟)

B. Missing Subsequence Sum

题目:

思路:

纯数学

我们首先想想如果没 k 限制怎么办呢?显然我们直接选择 2 的 i 次方即可,其中 i 为 1 ~ 19

但是现在有 k 怎么办呢?我们先尝试考虑构造 (1 ~ k-1),显然我们很容易就能构造出来,我们假设 k 的最高二进制位 为 j,那么显然塞入所有的 除了 j 的 1~19 都没问题,那么现在对于 1 ~ 2的j次方-1 可以随便构造,但是 (2^{j} ~ k-1)无法构造 

对此我们只需要假如 k - 2^{j} 即可,因为原来的值域为 1\rightarrow 2^{j}-1,那么加上这个就显然能构造到 k - 1了,那么对于 (k+1 ~ n) 的部分呢?

首先对于 k+1 我们是不能构造出来的,所以我们肯定要加入 k+1,那么现在值域就变成了 (1 ~ 2k),而其中 k 除外,由于 2*k > 2^{j} 是肯定的,所以对于大于 2^{j} 的数我们就能先用 1~2k 里的数凑小于 2^{j} 的部分,而对于大于的部分我们直接使用 (j+1 ~ 19) 的二进制位即可

但是有个特列,我们总结了这么多但是构造不出 2^{j },假设对于一个数 v,其大于 k,且其中含有 2^{j },那我们如何构造呢?

此时我们假设 v = k+1 + 2^{j},那么显然我们是无法凑出的,首先我们使用 1~k-1 的数显然是无法凑出 2^{j },所以即使我们选择 k+1 也是无法凑出 v 的,因此我们只能加入一个 k+1 + 2^{j},这样对于所有的数就能构造出来了

总结:

①对于 1~k-1 的数:

我们首先不需要考虑 k+1 之后的数以及幂次,因为选了那么肯定大于 k-1

对于 1~2^{j}-1 的数我们正常构造即可,对于 1~k-1,我们只需要选择上 k - 2^{j} 即可

(注意,由于我们没有加入2^{j},所以我们构造不出 k)

②.对于大于 k 的数:

由于大于 k,那么起码就是 k+1,而对于 k+1 我们是构造不出来的,所以要加入 k+1

对于其余数,我们可以使用其余数构造出 v - (k+1),即我们选完 k+1 后剩下什么数,如果这个数 x 还含有 2^{j},那么显然无法构造,所以我们还需要添加 k + 1 + 2^{j} (既然他大于 k 而且删去 k+1 后还有 2^{j},那我们就选这个即可)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define yes cout << "Yes\n"
#define no cout << "No\n"

void solve()
{
    int n, k;
    cin >> n >> k;

    int i = 0;
    while ((1 << (i + 1)) <= k)
    {
        i++;
    }

    vector<int> ans;
    ans.push_back(k - (1 << i));
    ans.push_back(k + 1);
    ans.push_back(k + 1 + (1 << i));

    for (int j = 0; j < 20; j++)
    {
        if (j != i)
        {
            ans.push_back(1 << j);
        }
    }

    cout << ans.size() << endl;
    for (int idx = 0; idx < ans.size(); idx++)
    {
        cout << ans[idx] << " ";
    }
    cout << endl;
}

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

B. Charming Meals

题目:

思路:

贪心

 首先我们可以给数组排序,为了保证最小值最大,那么肯定是尽可能的使用a小数匹配b大数,b小数匹配a大数

具体的,我们假设我们选择了 1~p 这一段满足 a 小数匹配 b 大数,及 (a[i], b[n-p+i]),其余数也是类似匹配,由于 n 很小,因此我们可以 n² 的算法过,即枚举位置 + 计算答案

对于贪心的证明:我们假设不是选择连续的区间进行匹配,而是跳着匹配,那么假设原来是 ai 配对 bj,现在变成了 ak 配对 bj,且 ak > ai,那么此时虽然值会更大,但原来的 ai 就只能往后匹配,那么以此类推,原来的数就会被挤走,最后导致更小的值产生(个人理解)

豆包学长的解释:

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define yes cout << "Yes\n"
#define no cout << "No\n"

void solve()
{
    int n;
    cin >> n;
    vector<int> a(n+1), b(n+1);
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    for (int i = 1; i <= n; i++)
    {
        cin >> b[i];
    }
    sort(a.begin()+1,a.end());
    sort(b.begin()+1,b.end());
    int ans = 0;
    auto getans = [&](int t){
        int res = 1e18;
        for (int i = 1; i <= t; i++)
        {
            res = min(res,abs(a[i] - b[n-t+i]));
        }
        for (int i = t+1; i <= n; i++)
        {
            res = min(res,abs(a[i]-b[i-t]));
        }
        ans = max(ans,res);

    };
    for (int i = 1; i <= n; i++)
    {   
        getans(i);
    }
    cout << ans << endl;
}

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

D. Umka and a Long Flight

题目:

思路:

斐波那契

其实这一题我们只需要知道怎么拼凑即可,具体看下面的图

我们取一部分来看

我们可以先删除

右边这一块,然后现在就剩里面的小的,我们将其翻转过来

会发现其实和上面的相似,我们又会删除右边的这个正方形,以此类推

显然这是一个递归的过程,具体的:每次我们删除一个大小为 宽*宽 的正方形,然后反转长宽,同时反转 X,Y,直到长为 1 

特别细节:我们可能删除的是左边的正方形,及此时 X,Y 在图中红点处

显然是没影响的,只不过删除之后我们就得让 X 减去宽,因为整体往左缩了(切走了一块)

对于无解的情况,如果 X >= l 显然是不行的(具体看代码,我们先看看到底切哪边,如果 X > l 说明我们切左边,否则切右边,如果切的是右边,如果二者都切不了那么就不行)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define yes cout << "YES\n"
#define no cout << "NO\n"

int f[50];
void solve()
{
    int n, Y, X;
    cin >> n >> Y >> X;
    int h = f[n + 1], l = f[n];
    while (l > 1)
    {
        if (X > l)
        {
            X -= l;
        }
        if(X <= h-l)
        {
            swap(h,l);
            swap(X,Y);
            l -= h;
        }
        else
        {
            no;
            return;
        }
    }
    yes;
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值