A - Round decimals
将给定的浮点数四舍五入。
\(round(x)\)
Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;
int main()
{
double n;
cin >> n;
cout << round(n) << "\n";
return 0;
}
B - Counting Arrays
有 \(n\) 个序列,求不同的序列个数?
\(set\) 去重
Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;
int n;
set<vector<int> > S;
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n;
while(n -- )
{
int k, x;
vector<int> v;
cin >> k;
while(k -- )
{
cin >> x;
v.push_back(x);
}
S.insert(v);
}
cout << (int)S.size() << "\n";
return 0;
}
C - Martial artist
有 \(n\) 个动作需要学,学习动作 \(i\) 需要时间 \(T_i\), 只有学完一个动作才能学另一个,动作之间有依赖关系,求学会动作 \(n\) 所需的最少时间?
建图,能够走到动作 \(n\) 的所有动作都需要学,不妨把 \(n\) 当作起点,遍历所有能到达的点。
Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 20;
int n, w[N];
vector<int> G[N];
bool st[N];
LL ans;
void DFS(int u)
{
st[u] = true;
ans += w[u];
for(auto v : G[u])
if(!st[v])
DFS(v);
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n;
for(int i = 1; i <= n; ++ i)
{
cin >> w[i];
int k, x;
cin >> k;
while(k -- )
{
cin >> x;
G[i].push_back(x);
}
}
DFS(n);
cout << ans << "\n";
return 0;
}
D - Teleportation
二维坐标系上有 \(n\) 个点,可以使用一种操作 \((a, b)\) , 从 \((x, y)\) 移动到 \((x + a, y + b)\) 。
若想在任意两个点之间互相到达,所需的最少操作种类数?
令 \(g = gcd(a, b)\), \((a, b)\) 一定可以由 \((\dfrac{a}{g}, \dfrac{b}{g})\) 凑出。
统计任意两点 \(i\) 和 \(j\) 间距离 \((x_i - x_j, y_i - y_j)\), 令 \(g_{i, j} = gcd(|x_i - x_j|, |y_i - y_j|)\),
答案即为 \((\dfrac{x_i - x_j}{g_{i,j}}, \dfrac{y_i - y_j}{g_{i,j}})\) 的不同种类数。
Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;
const int N = 500 + 10;
int n;
int x[N], y[N];
set<pair<int, int> > S;
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n;
for(int i = 1; i <= n; ++ i)
cin >> x[i] >> y[i];
for(int i = 1; i <= n; ++ i)
for(int j = 1; j <= n; ++ j)
{
if(i == j) continue;
int xx = x[i] - x[j], yy = y[i] - y[j];
int g = __gcd(xx, yy);
if(g < 0) g = -g;
xx /= g, yy /= g;
S.insert({xx, yy});
}
cout << S.size() << "\n";
return 0;
}
E - Just one
给定 \(n\) 个点 \(m\) 条边的简单无向图,每一条边可以给定一个方向,共有 \(2^m\) 中方案,统计每个点有且只有一条出边的方案数。
若某个连通分支只有单个环,贡献为 \(2\) , 其他情况均不合法,
问题转化为求只有单个环的联通分支个数 \(t\) , 答案即为 \(2^t\)
如何判断一个连通分支是否是单个环: 若无环,则 \(n\) 个点的分支有 \(n - 1\) 条边;若有一个环,则边数 \(+1\),有 \(n\) 条边;若有多个环,边数 \(\ge n\) ; 故只需要判断该联通分支点数和边数是否相等即可
Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 20;
const int P = 998244353;
int n, m;
vector<int> G[N];
bool st[N];
int point, edge;
void DFS(int u)
{
point ++;
edge += G[u].size();
st[u] = true;
for(auto v : G[u])
if(!st[v])
DFS(v);
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> m;
for(int i = 1; i <= m; ++ i)
{
int a, b;
cin >> a >> b;
G[a].push_back(b);
G[b].push_back(a);
}
int ans = 1;
for(int i = 1; i <= n; ++ i)
if(!st[i])
{
point = edge = 0;
DFS(i);
if(point * 2 == edge) ans = 2ll * ans % P;
else ans = 0;
}
cout << ans << "\n";
return 0;
}
F - Score of Permutations
对于一个给定的排列 \(P = (p_1, p_2, ..., p_n)\), 定义 \(S(P)\) 为:
初始时刻,每个人手中有一个球,每经过一轮,第 \(i\) 个人把手上的球传给 \(p_i\), \(S(P)\) 为至少经过一轮,每个人初始的球重新回到各自手上的最少轮数。
令 \(S_n\) 为 \((1, 2, ..., N)\) 的所有排列,求\(\sum\limits_{P\in S_n}S(P)^k \; mod\; 998244353\)
首先考虑对于一个排列 \(P\) , 如何计算它的 \(S(P)\) , 从 \(i\) 向 \(p_i\) 连一条边,可以发现球会在每一个环之间循环传递,假设形成了 \(k\) 个环,环的大小分别为 \((c_1,c_2,..., c_k)\), 当所有环都回到初始状态时, 所需的轮数为 \(lcm(c_1, c_2, ..., c_k)\)
考虑 \(dp\) 计算答案, 令 \(dp[i][j]\) 为给 \(i\) 个人已经分配好在哪个环中,且当前所有环的 \(lcm\) 为 \(j\) 的方案数,转移方程为:
\[dp[i + x][lcm(j, x)] = dp[i][j] * C_{n- i - 1} ^ {x - 1} * (x - 1)! \]对于上述状态转移方程,我们挑出 \(x\) 个人组成一个新的环,为了保证不会重复,可以给每次选出的环有序化,即每次先从剩下的人里面选择一个编号最小的,然后再从剩余的 \(n - i - 1\) 个人中选出 \(x - 1\) 个组成这个大小为 \(x\) 的环,即 \(C_{n - i - 1}^{x - 1}\)。对于选出的 \(x\) 个数,排列共有 \(x!\) 种, 又因为是环,所以有 \((x - 1)!\) 种方案。
由于 \(lcm\) 过大,状态转移可以用 \(map\) 实现; 初始状态为 \(dp[0][1] = 1\),答案即为 \(\sum dp[n][s] * s^k\)
Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int P = 998244353;
const int N = 50 + 5;
LL n, k, fac[N], C[N][N];
map<LL, LL> dp[N];
LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; }
LL lcm(LL a, LL b) { return a * b / gcd(a, b); }
LL power(LL a, LL b)
{
LL res = 1;
for(; b; b >>= 1, a = a * a % P)
if(b & 1) res = res * a % P;
return res;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
fac[0] = 1;
for(int i = 1; i < N; ++ i) fac[i] = fac[i - 1] * i % P;
for(int i = 0; i < N; ++ i)
for(int j = 0; j <= i; ++ j)
{
if(!j) C[i][j] = 1;
else C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % P;
}
cin >> n >> k;
dp[0][1] = 1;
for(int i = 0; i < n; ++ i)
for(auto [p, q] : dp[i])
for(int x = 1; x <= n - i; ++ x)
{
LL& d = dp[i + x][lcm(p, x)];
LL t = dp[i][p] * C[n - i - 1][x - 1] % P * fac[x - 1] % P;
d = (d + t) % P;
}
LL ans = 0;
for(auto [p, q] : dp[n])
ans = (ans + power(p, k) * q % P) % P;
cout << ans << "\n";
return 0;
}