Codeforces Round 894 (Div. 3)
这次div3,没开出来D太令人伤心了
D
题意:
假设你有 x 个冰淇淋球, 每两个冰淇淋球可以做一个双球冰淇淋,注意{1, 2}和{2, 1}是相同的,问你想做 n 种不同的双球冰淇淋最少需要多少个冰淇淋球
思路:
要想冰淇淋球最少肯定优先用不同的来组合,所以可以先处理出来小于等于 n 的最大C(2, x), 然后不够的我们往里面添加相同的数
比如{1, 2, 3}我们再往里面加入一个1
就会发现方案数加一, 再加一个2
方案数又加一, 因为,C(2, n + 1) - C(2, n) <= C(2, n + 1)
#include <bits/stdc++.h>
using namespace std;
#define more ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl "\n"
#define pb push_back;
const int N = 2e5 + 5, MOD = 1e9 + 7, inf = 0x3f3f3f3f;
const long long INF = 1e18;
typedef long long LL;
typedef unsigned long long usLL;
typedef pair<int, int> PII;
typedef pair<string, string> PSS;
typedef pair<int, string> PIS;
LL C(LL n)
{
return (n - 1) * n / 2;
}
inline void solve()
{
LL n; cin >> n;
LL l = 2, r = 2e10;
while (l < r)
{
int mid = (l + r + 1) / 2;
if (C(mid) <= n) l = mid;
else r = mid - 1;
}
if (C(l) == n) cout << l << endl;
else cout << n - C(l) + l << endl;
}
int main()
{
more;
int T;
cin >> T;
while (T--)
{
solve();
}
//solve();
return 0;
}
E
题意:
给你一个序列 A, 你可以从中选最多 m 个数,每选一个数都需要付出代价,代价为当前这个数与上次选择的那个数之间的距离乘以 d, 问你最大能获得的数字和是多少, 假定拿第一个数付出的代价为d
思路:
假设现在选了 k 个数,
inline void solve()
{
int n, m, d; cin >> n >> m >> d;
std::vector<int> a(n);
for (int i = 0; i < n; i++)
cin >> a[i];
priority_queue<int, vector<int>, greater<int>> q;
LL sum = 0, ans = 0;
for (int i = 0; i < n; i++)
{
if (a[i] < 0) continue;
q.push(a[i]);
sum += a[i];
if (q.size() > m) // 超出部分删掉
{
sum -= q.top();
q.pop();
}
ans = max(ans, sum - (LL)(i + 1) * d);
}
cout << ans << endl;
}
F
题意:
你有两个魔法,水魔法和火魔法。每秒你分别能获得 w, f点对应的魔力。
现在有 n 个怪物,每个怪物的强度为 k。强度为 k 的怪物需要 k 点魔力的任意魔法消灭。
现在施法不耗任何时间,请问几秒后可以杀完全部的怪物?
思路:
所有的怪最后都是一部分被水魔法打败,一部分被火魔法打败,那么可以枚举其中一部分,n 为 100 递归会爆,二进制枚举也会炸。
可以用bitset优化,一旦知道哪些怪是水魔法打败的,那么花费的时间就是
inline void solve()
{
int w, f; cin >> w >> f;
int n; cin >> n;
std::vector<int> s(n);
for (int i = 0; i < n; i++)
cin >> s[i];
std::bitset<N> dp{};
dp[0] = 1;
int sum = 0;
for (int i = 0; i < n; i++) // 预处理
{
dp |= dp << s[i];
sum += s[i];
}
int ans = inf;
for (int i = 0; i <= sum; i++)
{
if (dp[i]) // 说明i可以由一些s[i]加起来得到
{
ans = min(ans, max((i + w - 1) / w, (sum - i + f - 1) / f));
}
}
cout << ans << endl;
}
G
题意:
定义一个程序为:
- 将数组非降序排序并去重
- 数组中只有一个数时终止程序并输出这个数
- 给数组每个数加上n - i + 1, 下标从1开始
- 重复上述操作
现在有个q
个查询,每次输入x, y; 将a[x]改为y,并将数组输入程序输出最后的值
思路:
通过观察可以发现:
- 每次执行操作
3
之后,数组的差分数组会整体减一 - 差分为0的数会被去重
- 假设刚开始的时候最大的差分数值为d,那么操作3一共会被执行d次
- 当初始数组sort之后,最大值为
, 程序结束之后输出的值为 + d
所以我们需要查询数组最大值和最大差分值,可以O(n)查询,总的复杂度为O(q * n)超时
我们可以使用两个multiset来维护序列,s维护最大值,d维护最大差分
修改操作可以认为是删除a[x],再插入y
inline void solve()
{
int n; cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; i++)
cin >> a[i];
multiset<int> s, d{0};
auto add = [&](int x) // 插入一个数会影响它和前缀的差分,它和后缀的差分,前缀和后缀的差分
{
auto it = s.insert(x);
auto r = next(it);
if (it != s.begin()) // 存在前缀
{
d.insert(x - *prev(it));
}
if (r != s.end())
{
d.insert(*r - x);
}
if (it != s.begin() && r != s.end())
{
d.erase(d.find(*r - *prev(it))); // 要删除迭代器,不能直接删除数值,不然会把重复的也删掉
}
};
auto del = [&](int x)
{
auto it = s.find(x);
auto r = next(it);
if (it != s.begin())
{
d.erase(d.find(x - *prev(it)));
}
if (r != s.end())
{
d.erase(d.find(*r - x));
}
if (it != s.begin() && r != s.end())
{
d.insert(*r - *prev(it));
}
s.erase(it);
};
for (int i = 0; i < n; i++)
add(a[i]);
int q; cin >> q;
while (q--)
{
int x, y; cin >> x >> y;
x--;
del(a[x]);
a[x] = y;
add(a[x]);
cout << *s.rbegin() + *d.rbegin() << ' ';
}
cout << endl;
}
本文作者:自动机
本文链接:https://www.cnblogs.com/monituihuo/articles/17658892.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步