B. Missing Subsequence Sum
题目:
思路:
纯数学
我们首先想想如果没 k 限制怎么办呢?显然我们直接选择 2 的 i 次方即可,其中 i 为 1 ~ 19
但是现在有 k 怎么办呢?我们先尝试考虑构造 (1 ~ k-1),显然我们很容易就能构造出来,我们假设 k 的最高二进制位 为 j,那么显然塞入所有的 除了 j 的 1~19 都没问题,那么现在对于 1 ~ 2的j次方-1 可以随便构造,但是 ( ~ k-1)无法构造
对此我们只需要假如 k - 即可,因为原来的值域为
,那么加上这个就显然能构造到 k - 1了,那么对于 (k+1 ~ n) 的部分呢?
首先对于 k+1 我们是不能构造出来的,所以我们肯定要加入 k+1,那么现在值域就变成了 (1 ~ 2k),而其中 k 除外,由于 2*k > 是肯定的,所以对于大于
的数我们就能先用 1~2k 里的数凑小于
的部分,而对于大于的部分我们直接使用 (j+1 ~ 19) 的二进制位即可
但是有个特列,我们总结了这么多但是构造不出 ,假设对于一个数 v,其大于 k,且其中含有
,那我们如何构造呢?
此时我们假设 v = k+1 + ,那么显然我们是无法凑出的,首先我们使用 1~k-1 的数显然是无法凑出
,所以即使我们选择 k+1 也是无法凑出 v 的,因此我们只能加入一个 k+1 +
,这样对于所有的数就能构造出来了

总结:
①对于 1~k-1 的数:
我们首先不需要考虑 k+1 之后的数以及幂次,因为选了那么肯定大于 k-1
对于 1~ 的数我们正常构造即可,对于 1~k-1,我们只需要选择上 k -
即可
(注意,由于我们没有加入,所以我们构造不出 k)
②.对于大于 k 的数:
由于大于 k,那么起码就是 k+1,而对于 k+1 我们是构造不出来的,所以要加入 k+1
对于其余数,我们可以使用其余数构造出 v - (k+1),即我们选完 k+1 后剩下什么数,如果这个数 x 还含有 ,那么显然无法构造,所以我们还需要添加 k + 1 +
(既然他大于 k 而且删去 k+1 后还有
,那我们就选这个即可)
代码:
#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;
}
1588

被折叠的 条评论
为什么被折叠?



