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;
}
}

浙公网安备 33010602011771号