比赛链接

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. zookeeper 特点、使用场景及安装,配置文件解析

    本文为博主原创,未经允许不得转载: 1. Zookeeper 特点: ZooKeeper是用于分布式应用程序的协调服务.它公开了一组简单的API,分布式应用程序可以基于这些API用于同步,节点状态.配 ...

  2. spring--JDK动态代理和CGLIB代理的区别

    JDK 动态代理和 CGLIB 代理是 Java 中常用的两种动态代理实现方式,它们各有特点和适用场景: JDK 动态代理: JDK 动态代理是基于接口的代理方式,它使用 Java 反射机制来创建代理 ...

  3. 基于java+springboot的图书借阅网站-在线图书借阅管理系统

    该系统是基于java+springboot开发的图书借阅管理系统.是给师弟开发的课程作业.大家学习过程中,遇到问题可以github咨询作者. 系统演示地址 前台 http://book.gitapp. ...

  4. Laravel - 解决 $.ajax success 返回的数据为空的问题 (后台为laravel)

    原因之一 :  后台 dump()打印 , 去掉或者注释就好了

  5. [转帖]Linux中查看各文件夹大小命令du -h --max-depth=1

    https://www.cnblogs.com/the-tops/p/8798678.html 最近排查服务器异常的时候,常会遇到磁盘慢的情况,这个时候,查找那个文件夹占用的内存的时候常用到这个命令: ...

  6. [转帖]Linux的tmpfs和ramfs

    tmpfs tmpfs是一种虚拟内存文件系统, 它的存储空间在VM里面,现在大多数操作系统都采用了虚拟内存管理机制, VM(Virtual Memory) 是由Linux内核里面的VM子系统管理. V ...

  7. Nginx arm编译安装

    Nginx arm编译安装 背景 计划编译一套产品. 能够比较方便快捷的进行 nginx的交付. 主要思想是源码编译 不仅能够在arm上面运行 也可以在x86上面编译 考虑性能还有一些扩展性. 高效处 ...

  8. 【构造,图论,建模】Loj3629「2021 集训队互测」序列

    Problem Link 有一个长为 \(n\) 的未知序列,给定 \(m\) 个限制,每个限制形如给定 \(i,j,k,x\),要求 \(a_i,a_j,a_k\) 的中位数为 \(x\).构造一个 ...

  9. css hover频繁闪烁

    今天遇见一个问题. 在鼠标放上 图片上的时候. 删除图标一直不停的闪烁. 我当时觉得很奇怪,父子关系的结构 不应该闪烁呀. 看了下html和css,发现子元素(要hover)的元素是绝对定位了的 于是 ...

  10. golang 中使用 writev (sendmsg) 系统调用来一次发送多块数据

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 writev,或者说 sendmsg 等系统调用,能够发送 ...