2024CCPC哈尔滨 题解 ABCEGJKLM
A. 造计算机
构造题,注意到可以两点之间可以连不止一条边,所以我们可以创建一系列初始节点,让
所以我们可以将目标区间
具体实现可以枚举每一位,对于第
现在我们可以把得到的前缀插入到字典树中,跑一遍
由于每一段前缀都是
代码非常丑
#include <bits/stdc++.h>
constexpr int N = 22;
struct Trie
{
static constexpr int M = 2;
struct node
{
std::array<int, M> son;
bool isend;
std::set<int> len;
};
std::vector<node> tr;
int root = newnode();
int tot = 0;
int newnode()
{
tr.emplace_back();
return tr.size() - 1;
}
int insert(std::string &s, int len)
{
int p = root;
for(auto c : s)
{
int x = c - '0';
if(!tr[p].son[x]) tr[p].son[x] = newnode();
p = tr[p].son[x];
}
tr[p].isend = true;
tr[p].len.emplace(len);
return p;
}
};
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int l, r; std::cin >> l >> r;
std::vector<int> L, R;
bool ok = true;
if(l & 1) L.emplace_back(l), R.emplace_back(l);
for(int i = 0; i < N && (1 << i) < r; i++)
{
if(l >> i & 1) continue;
if(i == 0)
{
L.emplace_back(l);
R.emplace_back(l + 1);
continue;
}
int tl = (l >> i | 1) << i;
int tr = tl + (1 << i) - 1;
if(tr > r)
{
ok = false;
break;
}
L.emplace_back(tl), R.emplace_back(tr);
}
if(!ok) for(int i = 0; i < N && (1 << (i + 1)) < r; i++)
{
if(!(r >> i & 1)) continue;
int tl = (r >> (i + 1)) << (i + 1);
int tr = tl + (1 << i) - 1;
if(tl < l) break;
L.emplace_back(tl), R.emplace_back(tr | 1);
}
if(!std::ranges::count(R, r))
{
L.emplace_back(r);
R.emplace_back(r);
}
Trie trie;
int max = 0;
for(int i = 0; i < (int)R.size(); i++)
{
int len = 0;
std::string s = "";
while(L[i] || R[i])
{
if(L[i] % 2 != R[i] % 2) len++;
else s += std::to_string(L[i] % 2);
L[i] /= 2, R[i] /= 2;
}
std::ranges::reverse(s);
trie.insert(s, len);
max = std::max(max, len);
}
int cur = trie.tr.size();
std::vector<std::vector<std::pair<int, int>>> adj(cur + max + 1);
for(int i = 0; i < max; i++)
{
adj[cur + i].emplace_back(cur + i + 1, 0);
adj[cur + i].emplace_back(cur + i + 1, 1);
}
auto dfs = [&](auto dfs, int p) -> void
{
if(trie.tr[p].son[0])
{
int np = trie.tr[p].son[0];
if(trie.tr[np].isend) for(auto len : trie.tr[np].len)
adj[p].emplace_back(cur + max - len, 0);
adj[p].emplace_back(np, 0);
dfs(dfs, np);
}
if(trie.tr[p].son[1])
{
int np = trie.tr[p].son[1];
if(trie.tr[np].isend) for(auto len : trie.tr[np].len)
adj[p].emplace_back(cur + max - len, 1);
adj[p].emplace_back(np, 1);
dfs(dfs, np);
}
};
dfs(dfs, 0);
std::vector<std::vector<int>> ans;
std::vector<int> cnt(cur + max + 1);
for(int i = 0; i < cur + max + 1; i++)
{
if(adj[i].empty() && (i != cur + max)) cnt[i]++;
if(i) cnt[i] += cnt[i - 1];
}
for(int i = 0; i < cur + max + 1; i++)
{
if(adj[i].empty())
{
if(i == cur + max) ans.emplace_back(std::vector<int>{0});
continue;
}
int tot = adj[i].size();
for(auto [j, w] : adj[i]) if(adj[j].empty() && j != cur + max) tot--;
std::vector<int> tmp;
tmp.emplace_back(tot);
for(auto [j, w] : adj[i]) if(!adj[j].empty() || j == cur + max)
tmp.emplace_back(j - cnt[j] + 1), tmp.emplace_back(w);
ans.emplace_back(tmp);
}
std::cout << ans.size() << "\n";
for(auto vec : ans)
{
for(auto i : vec) std::cout << i << " ";
std::cout << "\n";
}
return 0;
}
B. 凹包
先对所有点求一次凸包,把这些点删除后再求一次凸包,那么只要最小化旧凸包上的一条边与新凸包上的一个点构成的三角形的面积集合,具体实现可以用双指针。
代码队友写的
#include <bits/stdc++.h>
using namespace std;
#define int long long
struct P{
int x, y;
P operator+(P p) { return {x + p.x, y + p.y}; }
P operator-(P p) { return {x - p.x, y - p.y}; }
bool operator<(P p) const{
if(x == p.x) return y < p.y;
return x < p.x;
}
bool operator==(P p)const{
return x == p.x && y == p.y;
}
int dot(P p){ return x * p.x + y * p.y; }
};
int cross(P p1, P p2, P p3){
return (p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y);
}
vector<P> convexHull(vector<P> &ps){
sort(ps.begin(),ps.end());
ps.erase(unique(ps.begin(), ps.end()), ps.end());
int n = ps.size();
if(n <= 1) return ps;
vector<P> qs(n << 1);
int top = 0;
for(int i = 0; i < n; qs[top++] = ps[i++])
while(top > 1 && cross(qs[top - 2], qs[top - 1], ps[i]) <= 0) top--;
for(int i = n - 2, t = top; i >= 0; qs[top++] = ps[i--])
while(top > t && cross(qs[top - 2], qs[top - 1], ps[i]) < 0) top--;
qs.resize(top - 1);
return qs;
}
void solve(){
int n;
cin >> n;
vector<P> a(n);
for(int i = 0; i < n; i++){
cin >> a[i].x >> a[i].y;
}
map<P, int> vis;
auto h1 = convexHull(a);
for(auto i : h1) vis[i] = 1;
vector<P> b;
for(auto i : a) if(!vis.contains(i)) b.push_back(i);
auto h2 = convexHull(b);
if(!h2.size()){
cout << -1 << "\n";
return;
}
int u = h1.size(), v = h2.size(), ans = 0;
for(int i = 2; i < u; i++){
ans += cross(h1[0], h1[i - 1], h1[i]);
}
int d = 9e18;
if(h2.size() == 1){
for(int i = 0; i < n; i++){
d = min(d, cross(h2[0], h1[i], h1[(i + 1) % u]));
}
cout << ans - d << "\n";
return;
}
for(int i = 0, j = 0; i < u; i++){
P nomral = {h1[i].y - h1[(i + 1) % u].y, h1[(i + 1) % u].x - h1[i].x};
while(nomral.dot(h2[(j + 1) % v] - h2[j]) < 0) j = (j + 1) % v;
while(nomral.dot(h2[(j + v - 1) % v] - h2[j]) < 0) j = (j + v - 1) % v;
d = min(d, cross(h2[j], h1[i], h1[(i + 1) % u]));
}
cout << ans - d << "\n";
}
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t; cin >> t;
while(t--) solve();
return 0;
}
C. 在哈尔滨指路
签到题,模拟即可
#include <bits/stdc++.h>
void solve()
{
int n; std::cin >> n;
std::map<std::pair<char, char>, char> mp;
mp[{'N', 'E'}] = 'R', mp[{'N', 'W'}] = 'L';
mp[{'S', 'E'}] = 'L', mp[{'S', 'W'}] = 'R';
mp[{'E', 'N'}] = 'L', mp[{'E', 'S'}] = 'R';
mp[{'W', 'N'}] = 'R', mp[{'W', 'S'}] = 'L';
char s; std::cin >> s;
int d; std::cin >> d;
std::vector<std::pair<char, int>> ans;
ans.emplace_back('Z', d);
char lst = s;
for(int i = 1; i < n; i++)
{
char c; std::cin >> c >> d;
ans.emplace_back(mp[{lst, c}], 0);
ans.emplace_back('Z', d);
lst = c;
}
std::cout << ans.size() << " " << s << "\n";
for(auto [dir, dis] : ans)
{
if(dis) std::cout << dir << " " << dis << "\n";
else std::cout << dir << "\n";
}
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t; std::cin >> t;
while(t--) solve();
return 0;
}
E. 弹珠赛跑
首先我们可以有一个朴素的想法,枚举所有存在小球位置可能为
如果我们每次都这么做一遍的话,时间复杂度显然为
这个时候我们可以思考一下,
其中
对于计算答案,在删除第
*
对于背包的操作,我们也可以从多项式的角度理解,我们可以把每个球看成一个多项式
记
相当于让原来的数组乘
可能还有其他理解方式,但是笔者太菜了只会这么化简
#include <bits/stdc++.h>
using i64 = long long;
constexpr int P = 1e9 + 7;
i64 power(i64 a, i64 b)
{
i64 res = 1;
for( ; b; b >>= 1, a = a * a % P)
if(b & 1) res = res * a % P;
return res;
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m; std::cin >> n >> m;
std::vector<i64> x(n), v(m);
for(int i = 0; i < n; i++) std::cin >> x[i];
for(int i = 0; i < m; i++) std::cin >> v[i];
std::vector<std::array<i64, 3>> t(n * m);
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
t[i * m + j] = {-x[i], v[j], j};
std::ranges::sort(t, [&](auto a, auto b)
{
return a[0] * b[1] < a[1] * b[0];
});
std::vector<int> cnt(m, n);
std::vector<i64> f(m + 1);
f[0] = 1;
auto mul = [&](i64 a, i64 b) -> void // a + bx;
{
auto g = f;
for(int i = 0; i <= m; i++) f[i] = g[i] * a % P;
for(int i = 1; i <= m; i++) f[i] = (f[i] + g[i - 1] * b % P) % P;
};
auto div = [&](i64 a, i64 b) -> void
{
i64 inva = power(a, P - 2);
for(auto &i : f) i = i * inva % P;
b = b * inva % P;
b = P - b;
for(int i = 0; i < m; i++) f[i + 1] = (f[i + 1] + f[i] * b) % P;
};
i64 ans = 0;
for(int i = 0; i < m; i++) mul(cnt[i], 0);
for(int i = 0; i < n * m; i++)
{
auto [a, b, id] = t[i];
div(cnt[id], n - cnt[id]);
cnt[id]--;
ans = (ans + a * power(b, P - 2) % P * f[m / 2] % P) % P;
mul(cnt[id], n - cnt[id]);
}
ans = ans * power(power(n, m), P - 2) % P;
std::cout << ans << "\n";
return 0;
}
G. 欢迎加入线上会议!
签到题。
随便选一个不忙的人开始跑
输出答案的时候可以重新跑一次
#include <bits/stdc++.h>
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m, k; std::cin >> n >> m >> k;
std::vector<int> tag(n), vis(n);
for(int i = 0; i < k; i++)
{
int x; std::cin >> x;
x--;
tag[x] = 1;
}
std::queue<int> q;
int st = 0;
for(int i = 0; i < n; i++)
{
if(!tag[i])
{
st = i;
q.emplace(i);
break;
}
}
std::vector<std::vector<int>> adj(n);
for(int i = 0; i < m; i++)
{
int u, v; std::cin >> u >> v;
u--, v--;
adj[u].emplace_back(v);
adj[v].emplace_back(u);
}
std::vector<std::vector<int>> ans(n);
int cnt = 1;
vis[st] = 1;
while(!q.empty())
{
int x = q.front(); q.pop();
for(auto y : adj[x])
{
if(vis[y]) continue;
ans[x].emplace_back(y);
vis[y] = 1;
cnt++;
if(!tag[y]) q.emplace(y);
}
}
if(cnt == n)
{
std::cout << "Yes\n";
int tot = 0;
for(int i = 0; i < n; i++) if(!ans[i].empty()) tot++;
std::cout << tot << "\n";
q.emplace(st);
while(!q.empty())
{
int x = q.front(); q.pop();
if(ans[x].empty()) continue;
std::cout << x + 1 << " " << ans[x].size() << " ";
for(auto y : ans[x])
{
std::cout << y + 1 << " ";
q.emplace(y);
}
std::cout << "\n";
}
}
else std::cout << "No\n";
return 0;
}
J. 新能源汽车
贪心的考虑,我们每次肯定用最近的充电桩的电池,所以可以记录每个电池对应的充电桩的位置,然后用一个小根堆存储每次备选的电池,枚举每个充电桩的位置,即时更新即可。
值得注意的是,一个电池必须还有点才能放入堆中,并且没有充电桩的电池也是可以使用一次的,所以我们可以给每个电池都虚设一个无限远处的充电桩。
#include <bits/stdc++.h>
using i64 = long long;
void solve()
{
int n, m; std::cin >> n >> m;
std::vector<int> a(n);
for(int i = 0; i < n; i++) std::cin >> a[i];
std::vector<int> aa(a);
std::vector<int> X(m), T(m);
for(int i = 0; i < m; i++) std::cin >> X[i] >> T[i], T[i]--;
std::vector<std::vector<int>> pos(n);
for(int i = 0; i < m; i++) pos[T[i]].emplace_back(X[i]);
for(int i = 0; i < n; i++)
{
pos[i].emplace_back(X[m - 1] + 1);
std::ranges::reverse(pos[i]);
}
i64 cur = 0;
std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int>>, std::greater<>> q;
for(int i = 0; i < n; i++)
{
q.emplace(pos[i].back(), i);
pos[i].pop_back();
}
for(int i = 0; i < m; i++)
{
int tmp = X[i] - cur;
while(!q.empty() && tmp)
{
auto [x, t] = q.top(); q.pop();
if(tmp >= a[t]) tmp -= a[t], a[t] = 0;
else
{
a[t] -= tmp;
tmp = 0;
if(x > X[i]) q.emplace(x, t); // 不是当前充电桩的电池,并且还能接着用,就重新入队
}
}
if(tmp)
{
std::cout << X[i] - tmp << "\n";
return;
}
cur = X[i];
a[T[i]] = aa[T[i]];
if(!pos[T[i]].empty()) // 又能用了,入队
{
q.emplace(pos[T[i]].back(), T[i]);
pos[T[i]].pop_back();
}
}
for(int i = 0; i < n; i++) cur += a[i];
std::cout << cur << "\n";
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t; std::cin >> t;
while(t--) solve();
return 0;
}
K. 农场经营
我们先对每种作物按
假如我们删除了作物
对每个
#include <bits/stdc++.h>
using i64 = long long;
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n; std::cin >> n;
i64 m; std::cin >> m;
std::vector<i64> w(n), l(n), r(n);
for(int i = 0; i < n; i++) std::cin >> w[i] >> l[i] >> r[i], m -= l[i];
std::vector<int> p(n);
std::iota(p.begin(), p.end(), 0);
std::ranges::sort(p, [&](int i, int j)
{
return w[i] > w[j];
});
std::vector<i64> pre(n + 1), suf(n + 1), res(n + 1);
for(int i = 0; i < n; i++)
{
res[i + 1] = res[i] + r[p[i]] - l[p[i]];
pre[i + 1] = pre[i] + w[p[i]] * r[p[i]];
}
for(int i = n - 1; i >= 0; i--)
suf[i] = suf[i + 1] + w[p[i]] * l[p[i]];
i64 ans = 0;
for(int i = 0; i < n; i++)
{
i64 ti = l[p[i]] + m;
if(ti >= res[i]) // 分的够直接分
{
ans = std::max(ans, pre[i] + w[p[i]] * (ti - res[i]) + suf[i + 1]);
continue;
}
int pos = *std::ranges::partition_point(std::views::iota(0, i), [&](int mid) // 找到第一个分不够的位置
{
return ti >= res[mid + 1];
});
ans = std::max(ans, pre[pos] + w[p[pos]] * (l[p[pos]] + ti - res[pos]) + suf[pos + 1] - w[p[i]] * l[p[i]]);
}
std::cout << ans << "\n";
return 0;
}
L. 树上游戏
详细题解
首先把期望转成统计方案数,最后除以
看到平方,套路地考虑维护
记
中的路径直接向答案贡献,这种情况要考虑 侧端点的方案数 中的路径和 中的路径拼起来,可以简单的计算
#include <bits/stdc++.h>
using i64 = long long;
void solve()
{
int n; std::cin >> n;
std::vector<int> fac;
for(int i = 1; i * i <= n; i++)
{
if(n % i) continue;
fac.emplace_back(i);
if(i * i != n) fac.emplace_back(n / i);
}
i64 ans = 0;
std::ranges::sort(fac);
for(int i = 0; i + 1 < (int)fac.size(); i++)
ans += 1LL * n / fac[i] * (fac[i + 1] - fac[i]);
std::cout << ans + 1 << "\n";
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t; std::cin >> t;
while(t--) solve();
return 0;
}
M. 奇怪的上取整
签到题,注意到
如果我们将
同时,
分解因数求解即可
#include <bits/stdc++.h>
using i64 = long long;
void solve()
{
int n; std::cin >> n;
std::vector<int> fac;
for(int i = 1; i * i <= n; i++)
{
if(n % i) continue;
fac.emplace_back(i);
if(i * i != n) fac.emplace_back(n / i);
}
i64 ans = 0;
std::ranges::sort(fac);
for(int i = 0; i + 1 < (int)fac.size(); i++)
ans += 1LL * n / fac[i] * (fac[i + 1] - fac[i]);
std::cout << ans + 1 << "\n";
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t; std::cin >> t;
while(t--) solve();
return 0;
}
都看到这了,点个赞支持一下笔者呗,这篇题解写了挺久的
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战