AtCoder Beginner Contest 243(E、F、G補題)
E - Edge Deletion
題意:
給定\(n\)點\(m\)邊的無向圖,現在問你最多刪除多少條邊,可以使得圖中的每對點的的最短距離不發生變化
思路:
根據資料範圍\(n≤300\)提示,可以發現需要用\(floyd\)演算法,算出每對點之間的最短距離後,然後列舉每條邊,如果當前邊的距離小於最短距離,說明當前邊可以被替代,如果當前點對的最短距離可以通過到某個中轉點,求得一樣的最短距離,說明當前邊可以被替代
View Code
#include <bits/stdc++.h> using namespace std; #define int long long const int inf = 1e18; const int N = 333; int n, m; int a[N * N], b[N * N], c[N * N]; int dis[N][N]; void floyd() { for (int k = 1; k <= n; k++) { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]); } } } } signed main() { cin >> n >> m; for (int i = 0; i <= n; i++) { for (int j = 0; j <= n; j++) { if (i == j) dis[i][j] = 0; else dis[i][j] = inf; } } for (int i = 1; i <= m; i++) { int u, v, w; cin >> u >> v >> w; a[i] = u, b[i] = v, c[i] = w; dis[u][v] = dis[v][u] = min(dis[u][v], w); } floyd(); int res = 0; for (int i = 1; i <= m; i++) { int u = a[i], v = b[i], w = c[i]; for (int k = 1; k <= n; k++) { if (dis[u][k] + dis[k][v] < w) { res++; break; } if (k != u && k != v && dis[u][k] + dis[k][v] == w) { res++; break; } } } cout << res << endl; }
F - Lottery
題意:
給定\(n\)個物品,數量無限制,每個物品的獲得的可能性為\(\frac{W_i}{\sum_1^nW_i}\),每個物品獲得的概率是獨立的,現在問拿\(K\)次,拿出\(M\)件不同的物品的概率為多少?
思路:
現在設每個物品拿出來\(c_i\)個,總共拿了\(k\)次,那麼拿出來的概率為\(p_1^{c_1}p_2^{c_2}....p_n^{c_n}\frac{k!}{c_1!c_2!...c_n!}\)
定義\(f(i,j,k)\)為考慮前\(i\)個物品,現在拿出\(j\)個不同的物品,現在總共拿了\(k\)個物品
轉移方程就是\(f(i+1,j+c!=0,k+c)=f(i+1,j+c!=0,k+c)+f(i,j,k)p_i^{c}/c!\)
最終答案就是\(f(n,m,k)\)
View Code
#include <bits/stdc++.h> using namespace std; #define int long long const int mod = 998244353; const int N = 55; int f[N][N][N]; int fact[N], infact[N]; int n, m, k; int w[N]; int p[N]; int pw[N][N]; int qmi(int a, int k, int p) { int res = 1; while (k) { if (k & 1) res = res * a % mod; k >>= 1; a = a * a % mod; } return res; } void init() { fact[0] = infact[0] = 1; for (int i = 1; i < N; i++) { fact[i] = fact[i - 1] * i % mod; } infact[N - 1] = qmi(fact[N - 1], mod - 2, mod) % mod; for (int i = N - 2; i >= 1; i--) { infact[i] = infact[i + 1] * (i + 1) % mod; } } signed main() { init(); cin >> n >> m >> k; int all = 0; for (int i = 1; i <= n; i++) { cin >> w[i]; all += w[i]; } int inv = qmi(all, mod - 2, mod) % mod; for (int i = 1; i <= n; i++) { p[i] = w[i] * inv % mod; pw[i][0] = 1; for (int j = 1; j < N; j++) { pw[i][j] = pw[i][j - 1] * p[i] % mod; } } f[0][0][0] = 1; for (int i = 1; i <= n; i++) { for (int j = 0; j <= m; j++) { for (int kk = 0; kk <= k; kk++) { for (int c = 0; kk - c >= 0; c++) { if (c == 0) { f[i][j][kk] += f[i - 1][j][kk] * pw[i][c] % mod * infact[c] % mod; f[i][j][kk] %= mod; } else { if (j >= 1) { f[i][j][kk] += f[i - 1][j - 1][kk - c] * pw[i][c] % mod * infact[c] % mod; f[i][j][kk] %= mod; } } } } } } int res = f[n][m][k] * fact[k] % mod; cout << res << endl; }
G - Sqrt
題意:
現在給定一個\(x\),現在每次可以從\(\sqrt{x}\)選擇一個數字插入到當前序列的尾部,然後在拿當前序列尾部這個數進行上述操作,問最終會形成多少序列
思路:
現在定義\(f(i)\)為當前數字以\(i\)結尾會形成多少個序列
區間\([t,t^{2}+2t]\)內的數字取根號是一樣的,所以就以根號為間距,進行計數
記當前根號區間為\([l,r]\)
那麼當前數字為\(x\),那麼\(f(x)=f(x)+(r-l+1)f(l)\)
最終答案就是\(f(n)\)
用unorder_map來記憶化,降低時間複雜度
View Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
unordered_map<int, int> f;
int F(int n) {
if (n == 1) return 1;
int x = sqrt((double)n);
if (f.count(n)) return f[n];
int res = 0;
for (int l = 1, r; l <= x; l = r + 1) {
int dex = sqrt((double)l);
r = min(x, dex * dex + 2 * dex);
res += (r - l + 1) * F(l);
}
f[n] = res;
return res;
}
signed main() {
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
cout << F(n) << endl;
}
}