比赛链接

A

题解

方法一

知识点:并查集,树形dp,背包dp。

因为需要路径中的最大值,因此考虑按边权从小到大加入图中,保证通过这条边产生贡献的点对已经全部出现。

在加边的同时进行树上背包,答案存在集合根节点里即可。

树上背包需要用到上下界限制的转移优化,能将复杂度从 \(O(n^3)\) 降到 \(O(n^2)\) ,基本思想是每个点对只在LCA处贡献一次。

时间复杂度 \(O(n^2)\)

空间复杂度 \(O(n^2)\)

方法二

知识点:Kruskal重构树,树形dp,背包dp。

对原图进行最小生成树性质的Kruskal重构,任意两点的LCA点权为路径最大值。

然后,在这棵重构树上进行dp,核心内容一致,区别在于原边权变为了点权,原子树大小变为叶子节点个数。

时间复杂度 \(O(n^2)\)

空间复杂度 \(O(n^2)\)

代码

方法一

#include <bits/stdc++.h>
using namespace std;
using ll = long long; struct DSU {
vector<int> fa;
vector<int> sz; DSU(int n = 0) { init(n); } void init(int n) {
fa.assign(n + 1, 0);
sz.assign(n + 1, 1);
iota(fa.begin(), fa.end(), 0);
} int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); } bool same(int x, int y) { return find(x) == find(y); } void merge(int x, int y) {
sz[y] += sz[x];
fa[find(x)] = find(y);
}
}; tuple<int, int, int> e[3007];
bool black[3007];
int cost[3007]; ll f[3007][3007];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1;i <= n;i++) cin >> black[i];
for (int i = 1;i <= n;i++) {
cin >> cost[i];
f[i][black[i]] = 0;
f[i][black[i] ^ 1] = -cost[i];
}
for (int i = 1;i <= n - 1;i++) {
int u, v, w;
cin >> u >> v >> w;
e[i] = { w,u,v };
} sort(e + 1, e + n);
DSU dsu(n);
for (int i = 1;i <= n;i++) {
auto [w, u, v] = e[i];
u = dsu.find(u);
v = dsu.find(v);
vector<ll> g(n + 1, -1e18);
for (int j = 0;j <= dsu.sz[u];j++) {
for (int k = 0;k <= dsu.sz[v];k++) {
ll val = 1LL * (j * (dsu.sz[v] - k) + k * (dsu.sz[u] - j)) * w;
g[j + k] = max(g[j + k], f[u][j] + f[v][k] + val);
}
}
for (int j = 0;j <= dsu.sz[u] + dsu.sz[v];j++) f[u][j] = g[j];
dsu.merge(v, u);
}
ll ans = 0;
for (int i = 0;i <= n;i++) ans = max(ans, f[dsu.find(1)][i]);
cout << ans << '\n';
return 0;
}

方法二

#include <bits/stdc++.h>
using namespace std;
using ll = long long; struct DSU {
vector<int> fa;
vector<int> sz; DSU(int n = 0) { init(n); } void init(int n) {
fa.assign(n + 1, 0);
sz.assign(n + 1, 1);
iota(fa.begin(), fa.end(), 0);
} int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); } bool same(int x, int y) { return find(x) == find(y); } void merge(int x, int y) {
sz[y] += sz[x];
fa[find(x)] = find(y);
}
}; int n;
struct edge {
int u, v, w;
}e[3007];
bool black[3007];
int cost[3007]; int g_w[6007];
vector<int> g[6007];
DSU dsu;
void kruskal_rebuild() {
sort(e + 1, e + n, [&](const edge &a, const edge &b) {return a.w < b.w;});
dsu.init(2 * n - 1);
for (int i = 1;i <= n - 1;i++) {
auto [u, v, w] = e[i];
u = dsu.find(u);
v = dsu.find(v);
g_w[n + i] = w;
g[n + i].push_back(u);
g[n + i].push_back(v);
dsu.merge(u, n + i);
dsu.merge(v, n + i);
}
}
/// kruskal重构树,O(mlogm),图重构为树后任意两点LCA的权值是路径瓶颈
//* 最小生成树 <=> u-v所有路径最大边权中的最小值 ll sz[6007], f[6007][3007];
void dfs(int u) {
sz[u] = u <= n;
for (auto v : g[u]) {
dfs(v);
vector<ll> ff(n + 1, -1e18);
for (int i = 0;i <= sz[u];i++) {
for (int j = 0;j <= sz[v];j++) {
ll val = 1LL * (i * (sz[v] - j) + j * (sz[u] - i)) * g_w[u];
ff[i + j] = max(ff[i + j], f[u][i] + f[v][j] + val);
}
}
for (int i = 0;i <= sz[u] + sz[v];i++) f[u][i] = ff[i];
sz[u] += sz[v];
}
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1;i <= n;i++) cin >> black[i];
for (int i = 1;i <= n;i++) {
cin >> cost[i];
f[i][black[i]] = 0;
f[i][black[i] ^ 1] = -cost[i];
}
for (int i = 1;i <= n - 1;i++) {
int u, v, w;
cin >> u >> v >> w;
e[i] = { u,v,w };
} kruskal_rebuild();
dfs(2 * n - 1); ll ans = 0;
for (int i = 0;i <= n;i++) ans = max(ans, f[2 * n - 1][i]);
cout << ans << '\n';
return 0;
}

B

题解

知识点:排列组合。

只有大小相同的多重子集才能产生贡献。对于一对子集,显然从小到大排序后,对应数字的差的绝对值的和就是最小操作次数,现在考虑枚举每个点对产生的贡献。

对于一组点对 \((i,j)\) 表示A中第 \(i\) 个数和B中第 \(j\) 个数在对应位置,那么包含它们的子集有:

\[\begin{aligned}
\left( \sum_{k = 0}^{\min(i-1,j-1)} \dbinom{i-1}{k} \dbinom{j-1}{k} \right) \cdot \left( \sum_{k = 0}^{\min(n-i,n-j)} \dbinom{n-i}{k} \dbinom{n-j}{k} \right)
\end{aligned}
\]

直接算是 \(O(n)\) 的,无法预处理,这里需要用到范德蒙德卷积公式:

\[\begin{aligned}
\sum_{i = 0}^{k} \dbinom{n}{i} \dbinom{m}{k-i} = \dbinom{n+m}{k}
\end{aligned}
\]

其中一个推论是:

\[\begin{aligned}
\sum_{i = 0}^{m} \dbinom{n}{i} \dbinom{m}{m-i} = \sum_{i = 0}^{m} \dbinom{n}{i} \dbinom{m}{i} = \dbinom{n+m}{m}
\end{aligned}
\]

因此原式可以化简为 \(O(1)\) 的计算。

时间复杂度 \(O(n^2)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; const int P = 998244353;
namespace Number_Theory {
const int N = 4e3 + 7;
int qpow(int a, ll k) {
int ans = 1;
while (k) {
if (k & 1) ans = 1LL * ans * a % P;
k >>= 1;
a = 1LL * a * a % P;
}
return ans;
}
int fact[N], invfact[N];
void init(int n) {
fact[0] = 1;
for (int i = 1;i <= n;i++) fact[i] = 1LL * i * fact[i - 1] % P;
invfact[n] = qpow(fact[n], P - 2);
for (int i = n;i >= 1;i--) invfact[i - 1] = 1LL * invfact[i] * i % P;
}
}
namespace CNM {
using namespace Number_Theory;
int C(int n, int m) {
if (n == m && m == -1) return 1; //* 隔板法特判
if (n < m || m < 0) return 0;
return 1LL * fact[n] * invfact[n - m] % P * invfact[m] % P;
}
}
/// 公式法求组合数,O(n),预处理阶乘及其逆元快速求出组合数 int a[2007], b[2007];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1;i <= n;i++) cin >> a[i];
for (int i = 1;i <= n;i++) cin >> b[i];
sort(a + 1, a + n + 1);
sort(b + 1, b + n + 1);
CNM::init(2 * n);
int ans = 0;
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
int val = abs(a[i] - b[j]);
(ans += 1LL * CNM::C(i + j - 2, min(i, j) - 1) * CNM::C(2 * n - i - j, min(n - i, n - j)) % P * val % P) %= P;
}
}
cout << ans << '\n';
return 0;
}

C

题解

知识点:数学。

显然,\(2\) 的个数远比 \(5\) 多,因此我们只需要计算 \(5\) 因子的个数即可。

我们化简后有如下式子:

\[\begin{aligned}
\prod_{i=1}^n i!! = 1^{\left\lceil \frac{n}{2} \right\rceil} \cdot 2^{\left\lfloor \frac{n}{2} \right\rfloor} \cdot 3^{\left\lceil \frac{n}{2} \right\rceil - 1} \cdot 4^{\left\lfloor \frac{n}{2} \right\rfloor - 1} \cdots
\end{aligned}
\]

注意到, \(5\) 的倍数会贡献一次, \(5^2\) 的倍数又会贡献一次,以此类推。因此,我们按 \(5\) 的幂求幂次数总和即可。

但是,这里奇数和偶数的幂次规律是不同的,但都是等差,分别求一下即可。例如, \(5\) 的倍数时,分别求 \(5,15,25,\cdots\) 和 \(10,20,30,\cdots\) 两个幂次等差数列的和即可。

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

空间复杂度 \(O(1)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using i128 = __int128_t; template<class T>
inline void write(T x) {
if (x < 0) { putchar('-');x = -x; }
if (x >= 10) write(x / 10);
putchar(x % 10 + '0');
} i128 calc1(i128 n, i128 x) {
i128 a1 = (n + 1) / 2 - x / 2;
i128 an = a1 % x;
i128 cnt = (a1 - an) / x + 1;
return (a1 + an) * cnt / 2;
} i128 calc0(i128 n, i128 x) {
i128 a1 = n / 2 - x + 1;
i128 an = a1 % x;
i128 cnt = (a1 - an) / x + 1;
return (a1 + an) * cnt / 2;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
ll n;
cin >> n;
i128 x = 5, ans = 0;
while (n >= x) {
if (n >= 2 * x) ans += calc0(n, x);
ans += calc1(n, x);
x *= 5;
}
write(ans);
puts("");
return 0;
}

E

题解

知识点:枚举,前缀和。

我们求出前缀和 \(sum\),那么原式改写为 \(sum_{b_1} - sum_{l-1},sum_{b_2} - sum_{b_1} \cdots\) 。显然,当 \(sum_{l-1}\) 和 \(sum_{r}\) 不同奇偶时无解,否则需要在 \([l,r-1]\) 的前缀和之间找到 \(k-1\) 个和 \(sum_{l-1},sum_{r}\) 同奇偶的位置,这个过程可以用 \(cnt\) 记录前缀和奇偶个数的前缀和,可以 \(O(1)\) 查询。

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; ll a[100007];
int cnt[100007];
bool solve() {
int n, q;
cin >> n >> q;
for (int i = 1;i <= n;i++) {
cin >> a[i];
a[i] += a[i - 1];
cnt[i] = (a[i] & 1) + cnt[i - 1];
}
while (q--) {
int l, r, k;
cin >> l >> r >> k;
if ((a[l - 1] & 1) != (a[r] & 1)) {
cout << "NO" << '\n';
continue;
}
int res = cnt[r - 1] - cnt[l - 1];
if (!(a[l - 1] & 1)) res = r - l - res;
if (res >= k - 1) cout << "YES" << '\n';
else cout << "NO" << '\n';
}
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

G

题解

知识点:GCD和LCM。

可以分三类讨论:

  1. 当 \(x,y = 0\) 时,当且仅当 \(z = 0\) 时有解。
  2. 否则,当 \(x = 0\) 或 \(y = 0\) 时,当且仅当 \(z\) 为其中非零数的倍数时有解。
  3. 否则,当且仅当 \(z\) 是 \(0\) 或者 \(\gcd(x,y)\) 的倍数时有解。

时间复杂度 \(O(T\log{\min\{x_i,y_i\}})\)

空间复杂度 \(O(1)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; bool solve() {
int x, y, z;
cin >> x >> y >> z;
int d = gcd(x, y);
if (x == 0 && y == 0) {
if (z == 0) cout << "YES" << '\n';
else cout << "NO" << '\n';
}
else if (x == 0 || y == 0) {
if (z % d == 0) cout << "YES" << '\n';
else cout << "NO" << '\n';
}
else {
if (z && z % d == 0) cout << "YES" << '\n';
else cout << "NO" << '\n';
}
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

2023牛客暑期多校训练营6 ABCEG的更多相关文章

  1. 2019牛客暑期多校训练营(第五场)G - subsequeue 1 (一题我真的不会的题)

    layout: post title: 2019牛客暑期多校训练营(第五场)G - subsequeue 1 (一题我真的不会的题) author: "luowentaoaa" c ...

  2. 2021牛客暑期多校训练营3 J 思维

    传送门 J-Counting Triangles_2021牛客暑期多校训练营3 (nowcoder.com) 题目 Goodeat finds an undirected complete graph ...

  3. B-xor_2019牛客暑期多校训练营(第四场)

    题意 给出n个数组(每组数个数不定),m个询问 l, r, x 序号在区间\([l,r]\)的每个数组是否都可以取出任意个数异或出x 题解 判断一个数组能否异或出x,是简单的线性基问题 判断多个线性基 ...

  4. 2019牛客暑期多校训练营(第九场)A:Power of Fibonacci(斐波拉契幂次和)

    题意:求Σfi^m%p. zoj上p是1e9+7,牛客是1e9:  对于这两个,分别有不同的做法. 前者利用公式,公式里面有sqrt(5),我们只需要二次剩余求即可.     后者mod=1e9,5才 ...

  5. 2019牛客暑期多校训练营(第一场)A题【单调栈】(补题)

    链接:https://ac.nowcoder.com/acm/contest/881/A来源:牛客网 题目描述 Two arrays u and v each with m distinct elem ...

  6. 2019牛客暑期多校训练营(第一场) B Integration (数学)

    链接:https://ac.nowcoder.com/acm/contest/881/B 来源:牛客网 Integration 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 5242 ...

  7. 2019牛客暑期多校训练营(第一场) A Equivalent Prefixes ( st 表 + 二分+分治)

    链接:https://ac.nowcoder.com/acm/contest/881/A 来源:牛客网 Equivalent Prefixes 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/ ...

  8. 2019牛客暑期多校训练营(第二场)F.Partition problem

    链接:https://ac.nowcoder.com/acm/contest/882/F来源:牛客网 Given 2N people, you need to assign each of them ...

  9. 2019牛客暑期多校训练营(第八场)E.Explorer

    链接:https://ac.nowcoder.com/acm/contest/888/E来源:牛客网 Gromah and LZR have entered the fifth level. Unli ...

  10. 2019牛客暑期多校训练营(第一场)A Equivalent Prefixes(单调栈/二分+分治)

    链接:https://ac.nowcoder.com/acm/contest/881/A来源:牛客网 Two arrays u and v each with m distinct elements ...

随机推荐

  1. Go 标准库之 io.Copy 和 ioutil.ReadAll

    1. go 标准库之 io.Copy 和 ioutil.ReadAll 1.1 介绍 go 标准库中通过 ioutil.ReadAll 实现数据流的读取,io.Copy 实现数据流的读取和写入. 那两 ...

  2. bitcask论文翻译/笔记

    翻译 论文来源:bitcask-intro.pdf (riak.com) 背景介绍 Bitcask的起源与Riak分布式数据库的历史紧密相连.在Riak的K/V集群中,每个节点都使用了可插拔的本地存储 ...

  3. [转帖]JVM随笔 --- 安全点(safe point)与 安全区域( safe region)

    https://zhuanlan.zhihu.com/p/461298916 11 人赞同了该文章 最近回顾 JVM safe point 与 safe region 又有一些新的感悟与收获,特别写篇 ...

  4. [转帖]GRUB2 配置文件详解

    https://www.cnblogs.com/fluidog/p/15176726.html 1. GRUB2配置文件 GRUB2 的配置文件通常为 /boot/grub2/grub.cfg,虽然此 ...

  5. [转帖]理解 Linux backlog/somaxconn 内核参数

    引言 在研究IOTDB的时候,启动服务的时候会有个报警. WARN: the value of net.core.somaxconn (=4096) is too small, please set ...

  6. [转帖]深入了解 gRPC:协议

    https://cn.pingcap.com/blog/grpc 经过很长一段时间的开发,TiDB 终于发了 RC3.RC3 版本对于 TiKV 来说最重要的功能就是支持了 gRPC,也就意味着后面大 ...

  7. [转帖]Fiddler抓取Chrome浏览器访问baiud.com报NET::ERR_CERT_COMMON_NAME_INVALID

    错误现象 解决方法: 1.Chrome浏览器地址栏中输:chrome://net-internals/#hsts 2.在Query HSTS/PKP domain处搜索www.baidu.com网站, ...

  8. [转帖]Kafka高可用 — KRaft集群搭建

    Apache Kafka Raft 是一种共识协议,它的引入是为了消除 Kafka 对 ZooKeeper 的元数据管理的依赖,被社区称之为 Kafka Raft metadata mode,简称 K ...

  9. [转帖]Day63_Kafka(一)

    第一讲 Kafka基础操作 课程大纲 课程内容 学习效果 掌握目标 Kafka简介 消息队列 掌握 Kafka简介 Kafka分布式环境 Kafka操作 Kafka shell 掌握 Kafka ap ...

  10. 【转帖】《MySQL高级篇》四、索引的存储结构

    1. 为什么使用索引 假如给数据使用 二叉树 这样的数据结构进行存储,如下图所示 2.索引及其优缺点 2.1 索引概述 2.2 优点 类似大学图书馆建书目索引,提高数据检索的效率,降低 数据库的 IO ...