SMU Summer 2024 Contest Round 8

Product

思路

注意到 \(\prod\limits_{i=1}^NL_i\le10^5\),也就是说 N 不会超过 16,因为 \(2^{17}>10^5\),所以我们可以直接暴搜。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); i64 n, x;
cin >> n >> x; vector L(n, vector<i64>());
for (int i = 0; i < n; i ++) {
int k;
cin >> k;
vector<i64> a(k);
for (auto &j : a)
cin >> j;
L[i] = a;
} i64 ans = 0;
auto dfs = [&](auto & self, int num, __int128 res)->void{ if (res > x) return; if (num == n) {
if (res == x)
ans ++;
return ;
} for (int i = 0; i < L[num].size(); i ++) {
self(self, num + 1, res * L[num][i]);
} }; dfs(dfs, 0, 1); cout << ans << '\n'; return 0;
}

Dice Sum

思路

考虑 dp。

设 \(dp_{i,j}\) 为前 i 个数中总和为 j 的方案数 则有转移方程:

\[dp_{i,j}=\sum\limits_{k=1}^{min(j,m)}dp_{i-1,j-k}
\]

上面是取模模板太长不放了。

代码

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); int n, m, k;
cin >> n >> m >> k; vector<vector<Z>> dp(n + 1, vector<Z>(k + 1)); for (int i = 1; i <= m; i ++)
dp[1][i] = 1; for (int i = 2; i <= n; i ++) {
for (int j = 1; j <= k; j ++)
for (int p = 1; p <= min(j, m); p ++) {
dp[i][j] += dp[i - 1][j - p];
}
} Z ans = 0;
for (int i = 0; i <= k; i ++)
ans += dp[n][i]; cout << ans << '\n'; return 0;
}

Ubiquity

思路

每一个位置有 10 种选择,总方案数为 \(10^N\) 种,选定一个为 0/9 的方案数有 \(9^N\) 种,直接相减会多减去 0 和 9 的方案数,需要加上 \(8^N\) 。

即:

\[10^N-2\times9^N+8^N
\]

上快速幂搞一下即可。

代码

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); int n;
cin >> n; cout << Z(power(Z(10), n) - 2 * power(Z(9), n) + power(Z(8), n)) << '\n'; return 0;
}

FG operation

思路

考虑 dp。

因为每次是删掉前两个数,所以考虑从左往右转移。

设 \(dp_{i,j}\) 表示为删掉第 i 个数后 \((x+y)\%10\) 为 j 的方案数。

转移方程为 :

\[dp_{i,(j+a_i)\bmod 10}=dp_{i,(j+a_i)\bmod 10}+dp_{i-1,j}
\]
\[dp_{i,(j\times a_i)\bmod 10}=dp_{i,(j\times a_i)\bmod 10}+dp_{i-1,j}
\]

代码

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); int n;
cin >> n; vector<i64> a(n + 1);
for (int i = 1; i <= n; i ++)
cin >> a[i]; vector<vector<Z>> dp(n + 1, vector<Z>(10)); dp[1][a[1]] = 1;
for (int i = 2; i <= n; i ++) {
for (int j = 0; j <= 9; j ++) {
dp[i][j * a[i] % 10] += dp[i - 1][j];
dp[i][(j + a[i]) % 10] += dp[i - 1][j];
}
} for (int i = 0; i <= 9; i ++) {
cout << dp[n][i] << '\n';
} return 0;
}

Left Right Operation

思路

前缀和后缀和枚举 L 和 R 两数更新前后缀更小的和,然后枚举 i 找到最小的答案即可。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); i64 n, l, r;
cin >> n >> l >> r; vector<i64> a(n + 1), pre(n + 1), suf(n + 2);
for (int i = 1; i <= n; i ++) {
cin >> a[i];
pre[i] = pre[i - 1] + a[i];
} for (int i = 0; i <= n; i ++) {
pre[i] = min(pre[i], i * l);
} for (int i = n; i >= 1; i --) {
suf[i] = min(suf[i + 1] + a[i], (n - i + 1) * r);
} i64 ans = 1e15;
for (int i = 0; i <= n; i ++) {
ans = min(ans, pre[i] + suf[i + 1]);
} cout << ans << '\n'; return 0;
}

Add and Mex

思路

根据 Mex 的性质,只有在 \([0,N]\) 的范围内才会对答案有贡献,且最大答案为 N + 1,又因为每次 +i,所以一个数会在第 \(\frac Ni\) 超过 N ,不再对答案产生贡献,因此我们可以求出一个数会在哪个区间内对答案产生贡献,即 \(0\le a_i+k\times i\le N\),得出 \(k\in [\lceil\frac{-a_i}{i}\rceil,\lfloor\frac{N-a_i}{i}\rfloor]\),这样枚举区间是一个调和级数级别的,即最终复杂度为 \(O(nln\ n)\) ,然后对于每一次询问暴力枚举区间内的 Mex 值即可。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); int n, m;
cin >> n >> m; vector idx(m + 1, set<int>()); for (int i = 1; i <= n; i ++) {
int a;
cin >> a;
int l = max((-a + i - 1) / i, 0), r = max((n - a) / i, 0);
a += l * i;
for (int j = l; j <= min(r, m); j ++, a += i) {
idx[j].insert(a);
}
} for (int i = 1; i <= m; i ++) {
int ans = 0;
while (idx[i].count(ans))
ans ++;
cout << ans << '\n';
} return 0;
}

Diameter set

思路

根据树的直径的性质:树的每一条直径一定经过一个公共点或一条公共边,经过的是点还是边取决于直径的长度为奇数还是偶数。

要找到相隔为 D 也就是树的直径的点的集合,就是要以树的中心为根,找到其子树内相差为 \(\frac D2\) 的点的集合。

树的直径为偶数的时候,这时候说明肯定存在一个中心点,找到离树的中心 \(\frac D2\) 的位置也就是找它子树中以离它儿子结点 \(\frac D2 -1\) 的位置,假设中心点为 mid,其儿子结点子树中共存在 x 个满足要求的集合,cnt 表示一个集合中的满足要求的点数,其中每个集合中只能选择一个点,因为同在一个子树里的两点其连成的路径不会经过树的中心,也就不可能为 D,其次集合中的点也可以不选作为一种方案,所以一个集合产生的贡献就是 cnt+1,所以总方案为 \(\prod\limits_{v\in son_{mid}}(cnt_v+1)\),根据题目要求至少要有两个点,所以我们要减去所有集合只选了一个点的方案数和所有点都没选的方案数,即最终答案就是 \(\prod\limits_{v\in son_{mid}}(cnt_v+1)-\sum\limits_{v\in son_{mid}}cnt_v-1\)。

树的直径为奇数的时候,这时候只存在一条中心边,所以答案只能由这条中心边的两点的子树来产生贡献,即两边的集合点数乘起来 \(cnt_1\times cnt_2\) ,这个时候不能加 1 了,因为只有两个集合,必须要选两个点也就是两个集合一边选一个。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

struct Tree {
int n, d = 0; //顶点,直径,树中心
//自顶向下u到其叶子结点最远距离d1,次长距离d2(与最长路径无公共边)
//p1,p2表示结点u向下更新时是由哪个结点更新来的
//up表示结点u向上到祖宗结点的最远距离
vector<int> d1, d2, p1, p2, up;
vector<vector<int>> g;
Tree(int n): n(n), d1(n + 1), d2(n + 1), g(n + 1), p1(n + 1), p2(n + 1), up(n + 1) {} void add(int u, int v) {
g[u].emplace_back(v);
g[v].emplace_back(u);
}
//求直径//自顶向下求u到叶子结点的最远距离
void dfs(int u, int fa) {
d1[u] = d2[u] = 0;
for (auto v : g[u]) {
if (v == fa)continue;
dfs(v, u);
auto t = d1[v] + 1;
if (t > d1[u]) {
d2[u] = d1[u], p2[u] = p1[u];
d1[u] = t, p1[u] = v;
} else if (t > d2[u]) {
d2[u] = t, p2[u] = v;
}
}
d = max(d, d1[u] + d2[u]);
}
//自底向上求u到其它结点的最长路径
void dfsup(int u, int fa) {
for (auto v : g[u]) {
if (v == fa) continue;
//如果父结点u向下的最长路径经过v
if (p1[u] == v) {
//结点v向上走到最长路径为
//父结点u继续向上的的最长路径和u向下走的次长路径的最大值+边权
up[v] = max(up[u], d2[u]) + 1;
} else {
//如果父结点u向下的最长路径不经过v
up[v] = max(up[u], d1[u]) + 1;
}
dfsup(v, u);
}
} //求树的直径
int FindDiameter() {
dfs(1, 0);
return d;
} //求树的中心
vector<int> FindCenter() {
dfsup(1, 0);
i64 res = INT_MAX;
vector<int> mid;
for (int i = 1; i <= n; i ++) {
auto t = max(d1[i], up[i]);
if (t < res) {
res = t;
vector<int>().swap(mid);
mid.emplace_back(i);
} else if (t == res) {
mid.emplace_back(i);
}
}
return mid;
} }; int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); int n;
cin >> n; Tree tr1(n);
for (int i = 1; i < n; i ++) {
int u, v;
cin >> u >> v;
tr1.add(u, v);
} const i64 mod = 998244353;
auto D = tr1.FindDiameter();
auto mid = tr1.FindCenter(); auto calc = [&](auto & self, int u, int fa, int dis, int x)->int{
int res = 0;
if (dis == x) res ++;
for (auto v : tr1.g[u]) {
if (v == fa)continue;
res += self(self, v, u, dis + 1, x);
}
return res;
}; if (D % 2 == 0) {
i64 ans = 1, no = 0, d = D / 2 - 1;
for (auto v : tr1.g[mid[0]]) {
int cnt = 0;
cnt = calc(calc, v, mid[0], 0, d);
ans = ans * (cnt + 1) % mod;
no += cnt;
}
cout << (ans - no - 1 + mod) % mod << '\n';
} else {
i64 d = D / 2;
i64 cnt1 = calc(calc, mid[0], mid[1], 0, d);
i64 cnt2 = calc(calc, mid[1], mid[0], 0, d);
cout << cnt1 * cnt2 % mod << '\n';
} return 0;
}

SMU Summer 2024 Contest Round 8的更多相关文章

  1. 2015 Astar Contest - Round 3 题解

    1001 数长方形 题目大意 平面内有N条平行于坐标轴的线段,且不会在端点处相交 问共形成多少个矩形 算法思路 枚举4条线段的全部组合.分别作为矩形四条边.推断是否合法 时间复杂度: O(N4) 代码 ...

  2. Contest Round #451 (Div. 2)F/Problemset 898F Restoring the Expression

    题意: 有一个a+b=c的等式,去掉两个符号,把三个数连在一起得到一个数 给出这个数,要求还原等式,length <= 1e6 三个数不能含有前导0,保证有解 解法: 铁头过题法,分类然后各种判 ...

  3. Codeforces Round #284 (Div. 2)A B C 模拟 数学

    A. Watching a movie time limit per test 1 second memory limit per test 256 megabytes input standard ...

  4. Sending messages to non-windowed applications -- AllocateHWnd, DeallocateHWnd

    http://delphi.about.com/od/windowsshellapi/l/aa093003a.htm Page 1: How Delphi dispatches messages in ...

  5. Codeforces 240 F. TorCoder

    F. TorCoder time limit per test 3 seconds memory limit per test 256 megabytes input input.txt output ...

  6. cf499B-Lecture 【map】

    http://codeforces.com/problemset/problem/499/B B. Lecture     You have a new professor of graph theo ...

  7. Codeforces 240F. TorCoder 线段树

    线段树统计和维护某一区间内的字母个数.. . . F. TorCoder time limit per test 3 seconds memory limit per test 256 megabyt ...

  8. 物联网学生科协第三届H-star现场编程比赛

    问题 A: 剪纸片 时间限制: 1 Sec 内存限制: 128 MB 题目描写叙述 这是一道简单的题目,假如你身边有一张纸.一把剪刀.在H-star的比赛现场,你会这么做: 1. 将这张纸剪成两片(平 ...

  9. [cf contest 893(edu round 33)] F - Subtree Minimum Query

    [cf contest 893(edu round 33)] F - Subtree Minimum Query time limit per test 6 seconds memory limit ...

  10. 水题 Codeforces Round #307 (Div. 2) A. GukiZ and Contest

    题目传送门 /* 水题:开个结构体,rk记录排名,相同的值有相同的排名 */ #include <cstdio> #include <cstring> #include < ...

随机推荐

  1. 如何在 VSCode 中配置和编写 LINGO

    目录 如何在 VSCode 中配置和编写 LINGO 安装 VSCode 扩展 LINGO 脚本文件与 runlingo 命令 LINGO 命令行交互和脚本文件 配置 Visual Stdio Cod ...

  2. Linux 增加 swap 分区

    检查当前swap分区 [root@localhost ~]# free -g total used free shared buffers cached Mem: 15 0 14 0 0 0 -/+ ...

  3. PPP协议简介

    转载出处:https://blog.csdn.net/csucxcc/article/details/1684416 PPP(Point-to-Point Protocol)协议是在SLIP的基础上发 ...

  4. 简约-Markdown教程

    ##注意 * 两个元素之间最好有空行 * 利用\来转义 我是一级标题 ==== 我是二级标题 ---- #我是一级标题 ##我是二级标题 ##<center>标题居中显示</cent ...

  5. AtCoder Beginner Contest 302 H. Ball Collector 题解 可撤销并查集

    为了更好的阅读体验,请单击这里 AtCoder Beginner Contest 302 H. Ball Collector 题意跳过. 可以视作将 \(a_i, b_i\) 之间连了一条边,然后 \ ...

  6. 超快的 Python 包管理工具「GitHub 热点速览」

    天下武功,无坚不破,唯快不破! 要想赢得程序员的欢心,工具的速度至关重要.仅需这一优势,即可使其在众多竞争对手中脱颖而出,迅速赢得开发者的偏爱.以这款号称下一代极速 Python 包管理工具--uv ...

  7. 疑难杂症(已解决) | 为什么出现python中tkinter创建界面需要一闪而过才进入主窗口?

    一.具体问题 如图所示,我编写了一个主窗口的程序(如下所示,有兴趣的可以自己复制后运行),发现需要先进入第一个窗口再进入主界面,这不符合逻辑. 代码区域(完整代码): from tkinter imp ...

  8. AT_agc017_b 题解

    洛谷链接&Atcoder 链接 本篇题解为此题较简单做法,请放心阅读. 题目简述 一共有 \(n\) 个格子,给定两个整数 \(A,B\) 分别位于第 \(1\) 和第 \(n\) 格,中间有 ...

  9. CF916C 题解

    CF916C 题解 思路 思考发现,如果我们让很多边的边权变得非常大,而故意留下 \(1\) 到 \(n\) 的某一条路径,使整条路径之和甚至还没有剩下一条边的权值大,这条路径显然就是最短路了. 更重 ...

  10. golang对遍历目录操作的优化

    一转眼go1.23都快发布了,时间过得真快. 不过今天我们把时间倒流回三年半之前,来关注一个在go1.16引入的关于处理目录时的优化. 对于go1.16的新变化,大家印象最深的可能是io包的大规模重构 ...