B. Not Sitting
题目:
思路:
似曾相识的题(社恐社牛选座位
题目的意思很简单,就是我们可以先让几个位置不让 A 选,然后再让A选,A选完后B可以随便坐,现在问他们两个的距离
我们先看简单情况,假设只有一行,那么显然我们只需要让 B 坐 1 或 n 的位置即可,因为 A 的最佳位置就是中间,所以我们坐两边即可,同理,如果只有一列我们也只需要坐两端
注意到题目的距离是 x 的差 + y 的差,也就是说二者是独立的,因此 B 的最佳选择肯定是坐四个角落,那 A 的位置呢?显然由于每次都是选择最佳的位置,所以我们可以枚举所有的位置到四个角落的距离,然后取最大值,那么我们排完序后按顺序输出即可
为什么呢?因为我们涂位置的最佳方式就是从距离最小的点开始涂,那么就是每次都会把一个 A 的最佳位置涂了,所以 A 每次都要往后选
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "YES\n"
#define no cout << "NO\n"
void solve()
{
int n, m;
cin >> n >> m;
multiset<int> ans;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
int mx = max(i, n - i - 1) + max(j, m - j - 1);
ans.insert(mx);
}
}
for (auto x : ans)
{
cout << x << " ";
}
cout << endl;
}
signed main()
{
cin.tie(0)->sync_with_stdio(false);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
D. Not Adding
题目:
思路:
数学题,思维
首先我们要知道一个结论就是 gcd(a,b,c) = gcd(gcd(a,b),c)
也就是说我们的gcd其实是固定的,那么我们就要考虑每一步的操作能不能造成奉献
观察到题目的数据很小,只有1e6,那我们可要尝试考虑打表
对于任意一个数 x,我们去看看数组中是否存在 x 的倍数,如果有 x 的倍数,那么最后只要选这几个倍数就可能能凑出 x,为什么是可能呢?因为有可能这几个倍数被gcd不一定是 x,可能是 kx,所以我们最后枚举完还要检查一下最后的 gcd 是不是 x,同时原数组中不能有 x
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "YES\n"
#define no cout << "NO\n"
int gcd(int a, int b)
{
return !b ? a : gcd(b, a % b);
}
void solve()
{
int n;
cin >> n;
vector<int> a(n);
vector<int> has(1e6 + 1, 0);
for (int i = 0; i < n; i++)
{
cin >> a[i];
has[a[i]] = 1;
}
int ans = 0;
for (int i = 1; i <= 1e6; i++)
{
int p = 0;
for (int j = i; j <= 1e6; j+=i)
{
if (has[j])
p = gcd(p, j);
}
if (p == i && !has[p])
ans++;
}
cout << ans;
}
signed main()
{
cin.tie(0)->sync_with_stdio(false);
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
C. Concatenation of Arrays
题目:
思路:
没啥意思的题
对于这题就是分类讨论,我们假设有两个 [a,b] [c,d],然后草稿纸分别枚举他们的大小关系和最后的结果,最后发现总和大的放后面是在每种情况中都是最优的,所以按这样排就行了
猜猜题
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "YES\n"
#define no cout << "NO\n"
void solve()
{
int n;
cin >> n;
vector<pair<int, int>> p(n);
for (int i = 0; i < n; i++)
{
cin >> p[i].first >> p[i].second;
}
sort(p.begin(), p.end(), [](pair<int, int> a, pair<int, int> b) {
return (a.first + a.second) < (b.first + b.second); });
for (auto x : p)
{
cout << x.first << " " << x.second << " ";
}
cout << endl;
}
signed main()
{
cin.tie(0)->sync_with_stdio(false);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
D. Skipping
题目:
思路:
非常好的题目,看RegenFallen佬的讲解无敌了
这题有两种做法,这里讲dp,我们先来分析一下题目
这题对于任意一个 i 题目我们都有两种选择
第一是写,写了就能获得其积分,然后再从 j < i 的问题中挑一个索引最大的没写过的问题当作下一个问题
第二是不写,如果不写我们就可以从 j < b[i] 的问题中挑一个索引最大的没写过的问题当作下一个问题
我们来简单分析一下,这题对于一个问题我们应该如何选呢,一个显然的思想是贪心,假设我们能跳到 j,那么对于所有 i <= j 的题目我们都能写了,所以最后总和是 sum[j] - cost,即前 j 个问题的总和减去跳到 j 的消耗
那么显然我们可以考虑动态规划,我们定义 dp[i] 为跳到 i 需要的费用,那我们就可以想如何转移了,观察题目对于一个点 i,我们可以跳到所有 j <= b[i] 的点,如果每个数都是一个很大的数,那我们每次的遍历绝对会炸掉,所以我们就要考虑优化
我们可以使用一个小顶堆的优先队列用来存储 “最便宜的跳跃方法”,即一个PII,前一个用于存储其费用,后面用于存储它最大能跳跃到的地方
所以我们就可以这样转移了,我们遍历每一个点 i,如果优先队列里的顶的 second 小于等于 i,说明可以跳到,那么就转移(这样的话肯定是最小花费)否则就丢掉,因为既然小于 i,那么i之后的也用不到了,所以可以丢掉,那么每次转移完我们可以看看这个点能不能跳到后面,如果可以那么就加入到队列中当作一个方案
注意,如果我们队列空了,那么说明就没有方法跳到 i 及其之后的点了,此时我们可以直接跳出循环
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "YES\n"
#define no cout << "NO\n"
#define PII pair<long long,long long>
void solve()
{
int n;
cin >> n;
vector<int> val(n+1), to(n+1);
for (int i = 1; i <= n; i++)
{
cin >> val[i];
}
for (int i = 1; i <= n; i++)
{
cin >> to[i];
}
vector<int> dp(n + 1, 1e16);
dp[1] = 0;
priority_queue<PII, vector<PII>, greater<>> pq;
pq.push({ 0,1 });//转移到第 1 个的花费是 0
for (int i = 1; i <= n; i++)
{
while (pq.size() && pq.top().second < i) pq.pop();
if (pq.empty()) break;
dp[i] = pq.top().first;
if(to[i] > i)
pq.push({ pq.top().first + val[i],to[i]});
}
int res = 0,sum = 0;
for (int i = 1; i <= n; i++)
{
sum += val[i];
res = max(res, sum - dp[i]);
}
cout << res << endl;
}
signed main()
{
cin.tie(0)->sync_with_stdio(false);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
C. Earning on Bets
题目:
思路:
数学题
我们可以先假定总和是 S,那么对于每一个点,我们起码要丢 S/ki 个硬币,所以就有
充分必要条件为:,为什么是小于呢,因为我们赢得钱要严格大于 S
那么S如何计算呢,由于我们每次投的都要是整数,那么也就是说 S 是每个 k 的倍数,所以我们只需要取 lcm(k1,k2,k3,...) 即可,然后按照上面的条件模拟即可
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "YES\n"
#define no cout << "NO\n"
int gcd(int a,int b)
{
return !b ? a : gcd(b, a % b);
}
int lcm(int a,int b)
{
return a * b / gcd(a, b);
}
void solve()
{
int n;
cin >> n;
vector<int> k(n),res(n);
for (int i = 0; i < n; i++)
{
cin >> k[i];
}
if (n == 1)
{
cout << "1\n";
return;
}
int LCM = lcm(k[0],k[1]);
for (int i = 2; i < n; i++)
{
LCM = lcm(LCM, k[i]);
}
int tot = 0;
for (int i = 0; i < n; i++)
{
res[i] = LCM / k[i];
tot += res[i];
}
if (tot >= LCM)
{
cout << "-1\n";
return;
}
for (auto x : res)
cout << x << " ";
cout << endl;
}
signed main()
{
cin.tie(0)->sync_with_stdio(false);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}