學習筆記—Generator和async/await
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;
}