当前位置: 首页 > news >正文

NOIP 模拟赛十一

A.

倒序贪心即可。

点击查看

#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
#define il inline
#define cmx(a, b) std::max(a, b)
#define cmn(a, b) std::min(a, b)
#define gmx(a, b) a = cmx(a, b)
#define gmn(a, b) a = cmn(a, b)template <typename T>
void _debug(const T& t) { std::cerr << t << '\n'; }
template <typename T, typename... Args>
void _debug(const T& t, const Args&...res) { std::cerr << t << ' '; _debug(res...); }
#define debug(...) _debug(#__VA_ARGS__ " =", __VA_ARGS__)const int LN = 1e6 + 7;
typedef long long ll;
typedef std::pair<ll, ll> PLL;bool FIRPOS;struct node { ll S, H, P;friend bool operator < (const node& x, const node& y) { return x.P < y.P; }
}a[LN];
int n; ll T, ans;
std::priority_queue <node> d;bool ENDPOS;int main() {std::ios::sync_with_stdio(false),std::cin.tie(nullptr), std::cout.tie(nullptr);int c1 = clock(); ll s, h, p;std::cin >> n >> T;lep(i, 1, n) {std::cin >> s >> h >> p, ans += h * p;if (!s) --i, --n;else a[i] = { s, h, p };}std::sort(a + 1, a + 1 + n, [](const node& x, const node& y) { return x.S < y.S; });ll t = 0; d.push(a[n]);rep(i, n - 1, 0) {t = a[i + 1].S - a[i].S;while (!d.empty() and t) {auto x = d.top(); d.pop();if (t >= x.H) ans -= x.H * x.P, t -= x.H;else ans -= t * x.P, x.H -= t, t = 0, d.push(x);} d.push(a[i]);}std::cout << (!ans) << ' ' << ans << '\n';
#ifdef DEBUGstd::cerr << clock() - c1 << " ms " << fabs(&ENDPOS - &FIRPOS) / 1024 / 1024 << " MB\n";
#endifreturn 0;
}

B.

打表发现 \(n\) 的转移点为 \(\frac{n}{p}\)\(p\) 为不大于 \(n\) 的最大质数。
\(g_n = p\)

\[f_i = f_{\frac{i}{g_i}} + \left\lfloor\frac{n}{\frac{i}{g_i}}\right\rfloor - g_i + 1 \]

将此过程看成树结构。
动态维护答案的增量,当前 \(n\) 的所有因子的子树(不含根)全部加一,维护子树大小即可。
树高是 \(\log\) 的,因为每次除去了一个质因子。

维护每个数的因子从 \(\frac{i}{g_i}\) 继承过来后再修改,访址更连续。

点击查看代码

#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
#define il inline
#define cmx(a, b) std::max(a, b)
#define cmn(a, b) std::min(a, b)
#define gmx(a, b) a = cmx(a, b)
#define gmn(a, b) a = cmn(a, b)template <typename T>
void _debug(const T& t) { std::cerr << t << '\n'; }
template <typename T, typename... Args>
void _debug(const T& t, const Args&...res) { std::cerr << t << ' '; _debug(res...); }
#define debug(...) _debug(#__VA_ARGS__ " =", __VA_ARGS__)const int LN = 2e6 + 7;
typedef long long ll;
typedef std::pair<int, int> PII;bool FIRPOS;int T, n, g[LN], sz[LN]; ll ans[LN];
std::vector <int> p[LN], prm; std::bitset <LN> vis;bool ENDPOS;ll jmp(int x, int n) {if (x != n) ++sz[x]; if (x == 1) return 1;return jmp(x / g[x], n) + n / (x / g[x]) + 1 - g[x];
}int main() {std::ios::sync_with_stdio(false),std::cin.tie(nullptr), std::cout.tie(nullptr);int c1 = clock(), x;lep(i, 2, LN - 1) {if (!vis[i]) prm.emplace_back(i);for (int j : prm) if (1ll * i * j < LN) {vis[i * j] = true;if (!(i % j)) break;} else break;}for (int j : prm) lep(i, 1, LN - 1) if (1ll * i * j < LN) g[i * j] = j; else break;p[1].emplace_back(1);lep(i, 2, LN - 1) {for (auto j : p[i / g[i]]) {p[i].emplace_back(j);if ((i / g[i]) % (g[i] * j)) p[i].emplace_back(g[i] * j);}}ans[1] = 1;lep(i, 2, LN - 1) {ans[i] = ans[i - 1];for (auto v : p[i]) ans[i] += sz[v];ans[i] += jmp(i, i);}std::cin >> T;while (T--) std::cin >> n, std::cout << ans[n] << '\n';#ifdef DEBUGstd::cerr << clock() - c1 << " ms " << fabs(&ENDPOS - &FIRPOS) / 1024 / 1024 << " MB\n";
#endifreturn 0;
}

C.

最小化 \(max + min\) 太难维护了,考虑枚举 \(max\)
将边排序后依次加入,然后对于每个与 \(1\) 连通的点,更新答案为 \(w + len\)
其中, \(w\) 是边权, \(len\) 是到 \(1\) 的最小值。
维护到根最小的最小值可以用边双树,边双内的最小边是可以随便经过的,割边也是好维护的。

我们对每条边分类讨论。

  • 在最小生成树上

    • \(1\) 不在任意一个连通块内

      合并即可,不需要额外处理。

    • 否则,暴力更新另一个连通块内的答案。我们可以通过维护每个点到 \(1\) 的最短链来做到这件事。

      复杂度是 \(O(n)\) 的。

  • 不在最小生成树上且不在一个边双内

    将这个边双暴力合并,维护最短链。

    被这个过程更新答案的点一定在最小生成树上边双的根(深度最小的点)的子树内。

提前建好最小生成树,使用线段树维护 dfn 序上的答案即可。

注意到在第二个过程中,如果我们严格更新边双所在连通块的答案是很难的,所以我们直接对整个子树全部更新。

\(1\) 最短链的维护是不会出问题的,而答案我们只需要在每个连通块第一次和根连通暴力更新答案时覆盖原来的即可。

这样只有与 \(1\) 连通之后这样的更新是有效的。

复杂度 \(O(n\log n)\)

点击查看

#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
#define il inline
#define cmx(a, b) ((a) > (b) ? (a) : (b))
#define cmn(a, b) ((a) < (b) ? (a) : (b))
#define gmx(a, b) a = cmx(a, b)
#define gmn(a, b) a = cmn(a, b)template <typename T>
void _debug(const T& t) { std::cerr << t << '\n'; }
template <typename T, typename... Args>
void _debug(const T& t, const Args&...res) { std::cerr << t << ' '; _debug(res...); }
#define debug(...) _debug(#__VA_ARGS__ " =", __VA_ARGS__)const int LN = 2e6 + 7;
const int inf = 2e9;
typedef long long ll;
typedef std::pair<int, int> PII;bool FIRPOS;int n;
struct seg {int mn[LN << 2], tag[LN << 2];
#define ls p << 1
#define rs p << 1 | 1
#define md ((s + t) >> 1)void bd(int s = 1, int t = n, int p = 1) {mn[p] = tag[p] = inf; if (s == t) return;bd(s, md, ls), bd(md + 1, t, rs);}il void pu(int p) { mn[p] = cmn(mn[ls], mn[rs]); }il void upd(int p, int k) { gmn(mn[p], k), gmn(tag[p], k); }il void pd(int p) { if (tag[p] != inf) upd(ls, tag[p]), upd(rs, tag[p]), tag[p] = inf; }void col(int d, int v, int s = 1, int t = n, int p = 1) {if (s == t) return mn[p] = v, void(); pd(p);d <= md ? col(d, v, s, md, ls) : col(d, v, md + 1, t, rs); pu(p);}void mdy(int l, int r, int v, int s = 1, int t = n, int p = 1) {if (r < s or t < l) return; if (l <= s and t <= r) return upd(p, v); pd(p);mdy(l, r, v, s, md, ls), mdy(l, r, v, md + 1, t, rs); pu(p);}int qry(int d, int s = 1, int t = n, int p = 1) {if (s == t) return mn[p]; pd(p);return d <= md ? qry(d, s, md, ls) : qry(d, md + 1, t, rs);}
#undef ls
#undef rs
#undef md
}loc, ans; int dfn[LN], idx, sz[LN], pr[LN], dep[LN];
struct edge { int u, v, w; }E[LN]; int m;
std::vector <int> e[LN], g[LN]; std::bitset <LN> tr;
int up[LN];
struct Dsu{int fa[LN]; std::vector <int> scc[LN];int fnd(int x) { return fa[x] == x ? x : fa[x] = fnd(fa[x]); }bool mrg(int u, int v) {if ((u = fnd(u)) == (v = fnd(v))) return false;if (scc[u].size() < scc[v].size()) std::swap(u, v);for (int t : scc[v]) scc[u].emplace_back(t); scc[v].clear();if (dep[u] > dep[v]) std::swap(scc[u], scc[v]), fa[u] = v;else fa[v] = u;return true;}
}grp, scc;bool ENDPOS;il void add(int u, int v, int w) { e[u].emplace_back(v), g[u].emplace_back(w); }
il void Add(int u, int v, int w) { add(u, v, w), add(v, u, w); }
void init(int u, int fa) {dfn[u] = ++idx, dep[u] = dep[pr[u] = fa] + 1, sz[u] = 1; int v;lep(k, 0, (int)e[u].size() - 1) { v = e[u][k];if (v != fa) init(v, u), sz[u] += sz[v], up[v] = g[u][k];}e[u].clear(), g[u].clear(), grp.scc[u].emplace_back(grp.fa[u] = scc.fa[u] = u);
}int main() {std::ios::sync_with_stdio(false),std::cin.tie(nullptr), std::cout.tie(nullptr);int c1 = clock();int u, v, w;std::cin >> n >> m;lep(i, 1, n) grp.fa[i] = i, up[i] = inf;lep(i, 1, m) std::cin >> u >> v >> w, E[i] = { u, v, w };std::sort(E + 1, E + 1 + m, [](const edge& x, const edge& y) { return x.w < y.w; });lep(i, 1, m) if (grp.mrg(E[i].u, E[i].v)) tr.set(i), Add(E[i].u, E[i].v, E[i].w);init(1, 0);lep(i, 2, n) debug(pr[i], i);loc.bd(), ans.bd();lep(i, 1, m) {u = E[i].u, v = E[i].v, w = E[i].w;if (scc.fnd(u) == scc.fnd(v)) continue;if (dep[u] > dep[v]) std::swap(u, v);if (tr[i]) {loc.mdy(dfn[v], dfn[v] + sz[v] - 1, cmn(w, loc.qry(dfn[u])));if (grp.fnd(u) == grp.fnd(1))for (auto p : grp.scc[v]) ans.col(dfn[p], loc.qry(dfn[p]) + w);grp.mrg(u, v);continue;}int d = inf; u = scc.fnd(u), v = scc.fnd(v);while (u != v) {if (dep[u] < dep[v]) std::swap(u, v);gmn(d, up[u]), scc.mrg(u, pr[u]), u = scc.fnd(pr[u]);} gmn(up[u], d);if (up[u] != inf) loc.mdy(dfn[u], dfn[u] + sz[u] - 1, up[u]),ans.mdy(dfn[u], dfn[u] + sz[u] - 1, up[u] + w);}lep(i, 2, n) std::cout << ans.qry(dfn[i]) << '\n';#ifdef DEBUGstd::cerr << clock() - c1 << " ms " << fabs(&ENDPOS - &FIRPOS) / 1024 / 1024 << " MB\n";
#endifreturn 0;
}

D.

我们将问题放到值域上来考虑。

每处的高地有两种选择,接到前面的枢纽上,或者花费代价延迟决策。

所以我们就记录下 \(f[i, j, k]\) 表示考虑了 \([1, i]\) 的值域,目前有 \(j\) 个枢纽,\(k\) 个高地延迟决策。

注意到如果我们将一个高地留在这个地方,后面可供选择的枢纽数是不变的,因为其自带一个枢纽。

所以我们肯定会将能够放下的所有高地全部留下。

也就是说,记 \(cnt_i\) 为等级为 \(i\) 的高地数目,则 \(f[i, j, k]\) 能够转移到 \(f[i + 1, j, k + cnt[i] - j]\)

然后再完全背包考虑一下当前新买一些枢纽给后面用,费用就是前缀最小值。

因为多个最小值时根是不固定的,所以我们新建一个拥有一个枢纽的虚点做根就可以了。

但是值域太大了,我们很自然地想离散化。

考虑当相邻两个 \(cnt\) 之间有空余会发生什么,肯定是每抬升一步,就会留下 \(j\) 个高地,因为这样的决策是一定不劣的。

我们可以通过等差数列求和轻松算出可以转移到哪个状态。

同时注意,在 \(i\) 处买的枢纽在 \(i+1\) 便可以用了,所以我们将所有的 \(a_i+1\) 也纳入决策点就行了。

复杂度 \(O(n^3)\)

点击查看

#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
#define il inline
#define cmx(a, b) std::max(a, b)
#define cmn(a, b) std::min(a, b)
#define gmx(a, b) a = cmx(a, b)
#define gmn(a, b) a = cmn(a, b)
#define int __int128template <typename T>
void _debug(const T& t) { std::cerr << t << '\n'; }
template <typename T, typename... Args>
void _debug(const T& t, const Args&...res) { std::cerr << t << ' '; _debug(res...); }
#define debug(...) _debug(#__VA_ARGS__ " =", __VA_ARGS__)const int LN = 1000 + 7;
typedef long long ll;
typedef std::pair<int, int> PII;bool FIRPOS;const ll inf = 1e12;
int n, a[LN], b[LN], B[LN], cnt[LN]; int P, f[2][LN][LN];
std::vector <int> Ab; int V;bool ENDPOS;int read() {int x = 0; char c = getchar();while (c < '0' or c > '9') c = getchar();while (c>= '0' and c<='9') x = (x << 3) + (x << 1) + c - '0', c = getchar();return x;
}signed main() {std::ios::sync_with_stdio(false),std::cin.tie(nullptr), std::cout.tie(nullptr);int c1 = clock();n = read(), P = read(); Ab.emplace_back(-1), Ab.emplace_back(0);lep(i, 1, n) a[i] = read(), b[i] = read(), Ab.emplace_back(a[i]), Ab.emplace_back(a[i] + 1);std::sort(Ab.begin(), Ab.end()), Ab.erase(std::unique(Ab.begin(), Ab.end()), Ab.end());V = Ab.size(), Ab.emplace_back((int)2e9);std::memset(B, 0x3f, sizeof(B));lep(i, 1, n) {a[i] = std::lower_bound(Ab.begin(), Ab.end(), a[i]) - Ab.begin();++cnt[a[i]], gmn(B[a[i]], b[i]);}int p = 0, nk, dl; std::memset(f, 0x3f, sizeof(f));f[p][1][0] = 0;lep(i, 0, V - 1) {std::memset(f[p ^ 1], 0x3f, sizeof(f[p ^ 1]));lep(j, 1, n) lep(k, 0, n) if (f[p][j][k] < inf) {nk = cmx((int)0, k + cnt[i] - j);dl = (nk + j - 1) / j;if (Ab[i] + dl < Ab[i + 1]) gmn(f[p ^ 1][j][0], f[p][j][k] + (j * (dl + 1) * dl / 2 - (dl * j - nk) * dl) * P);else dl = Ab[i + 1] - Ab[i] - 1, nk -= dl * j, gmn(f[p ^ 1][j][nk], f[p][j][k] + (j * dl * (dl + 1) / 2 + nk * (dl + 1)) * P);} p ^= 1;lep(k, 0, n) lep(j, 1, n) gmn(f[p][j][k], f[p][j - 1][k] + B[i]);}int ans = inf;lep(i, 0, n) gmn(ans, f[p][i][0]);std::cout << (ll)ans << '\n';return 0;
}

http://www.hskmm.com/?act=detail&tid=12089

相关文章:

  • Proxy 库解析(四)
  • warm-flow 监听器对象获取问题
  • Hexo Butterfly 5.4 分页问题 YAML 错误 解决方法总结
  • js逆向:某Q音乐平台请求数据模拟生成
  • maven
  • 第十一届中国大学生程序设计竞赛网络预选赛(CCPC Online 2025)
  • 网络流
  • 完整教程:数据结构 栈和队列、树
  • 深入解析:【ubuntu】ubuntu中找不到串口设备问题排查
  • 酵母双杂交技术:高通量筛选的突破与不可忽视的三大局限性
  • ubuntu20.04测试cuda
  • Android Studio 配置国内源
  • PyCharm项目上传GitHub仓库(笔记) - 教程
  • 从RAG出发
  • 软件工程第二次作业——第一次个人编程作业
  • Ubuntu 24.04 安装 DaVinci Resolve
  • Promise中处理请求超时问题
  • 图解26:老生常谈的OSI网络模型
  • 【C++】指针
  • AI驱动建筑行业数字化转型
  • 详细介绍:前端学习——CSS
  • VSCode 把代码发送到激活状态下的终端
  • 线性结构之数组[基于郝斌课程]
  • 完整教程:Vue中的props方式
  • 图解25:MySQL主从复制原理
  • 用 Go 编写验证码识别脚本(基于 Tesseract)
  • 软工第二次作业
  • Zero-Shot、One-Shot、Few-Shot概念
  • ADS放入元器件include和DK.zip文件依然提示未定义
  • AI元人文(十三):良知觉醒——论三值伦理模型与元道德主体的诞生