比赛链接

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. uniapp 子页面 滚动监听 是否到底

    主要属性:  handleScrollToLower <template> <view class="menu"> <scroll-view id=& ...

  2. 查看正在运行容器的环境变量-寻找容器运行mysql的root密码

    查看正在运行容器的环境变量-寻找容器运行mysql的root密码 背景 有一个服务器上面运行着一个长达两年的mysql数据库实例. 因为当时root密码是通过环境变量注入进去的. 现在我想重新连接一下 ...

  3. [转帖]Python执行Linux系统命令的4种方法

    https://www.cnblogs.com/pennyy/p/4248934.html (1) os.system 仅仅在一个子终端运行系统命令,而不能获取命令执行后的返回信息 代码如下: sys ...

  4. [转帖]TiDB的tikv节点的压缩算法

    简介:TiDB的tikv节点实用的RocksDB,RocksDB的默认压缩算法为:[no:no:lz4:lz4:lz4:zstd:zstd] RocksDB 每一层数据的压缩方式,可选的值为:no,s ...

  5. [转帖]【jmeter】BeanShell用法详细汇总

    一.什么是Bean Shell BeanShell是用Java写成的,一个小型的.免费的.可以下载的.嵌入式的Java源代码解释器,具有对象脚本语言特性,非常精简的解释器jar文件大小为175k. B ...

  6. [转帖]DD硬盘性能相关因素

    https://www.jianshu.com/p/a15d7a65c876 本文简单介绍下DD测试硬盘性能时,各个因素的影响 首先列出测试结果   image.png oflag分析--/home ...

  7. [转帖]记录几个常用linux命令的使用方法——find、grep、file、which、whereis和压缩命令gzip、bzip2、tar

    一.命令1: find.grep.file.which.whereis 1.find 目的:查找符合条件的文件 1)在哪些目录中查找 2)查找的内容 格式: find 目录名 选项 查找条件 举例: ...

  8. [转贴]一图弄懂ASCII、GB2312、GBK、GB18030编码

    一图弄懂ASCII.GB2312.GBK.GB18030编码 https://blog.csdn.net/LightUpHeaven/article/details/92008630 转载君子不器.  ...

  9. 【计数,DP】CF1081G Mergesort Strikes Back

    Problem Link 现有一归并排序算法,但是算法很天才,设了个递归深度上限,如果递归深度到达 \(k\) 则立即返回.其它部分都和正常归并排序一样,递归中点是 \(\lfloor (l+r)/2 ...

  10. 【小测试】VictoriaMetrics中如何汇总单个time series上的多个data point?

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 问题最终在andy专家的帮助下解决,但是内部的原理还是很迷 ...