1. 程式人生 > 其它 >AtCoder Beginner Contest 243(E、F、G補題)

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