最简单 概率dp

https://www.luogu.com.cn/problem/P4550
首先我们可以推出已经拿了i个不同牌的期望拿取次数

\[f[i] = \frac i n (f[i] + 1) + \frac {n - i} n (f[i + 1] + 1) \]

那么考虑拿了i个不同牌的期望花费,期望花费就可以看作增加的期望拿取次数是\(1\)变成了花费 \(k\) 就是\(f[i] + 1 和 f[i + 1] + 1\),因为题目说第\(k\) 局的花费是\(k\)

\[g[i] = \frac i n (g[i] + f[i] + 1) + \frac {n - i} n (g[i + 1] + f[i + 1] + 1) \]

double f[maxn], g[maxn];
void run() {
    int n;  scanf("%d", &n);
    for(int i = n - 1; i >= 0; -- i) {
        f[i] = f[i + 1] + 1.0 * n / (n - i);
        g[i] = 1.0 * i / (n - i) * f[i] + 1.0 * n / (n - i) + g[i + 1] + f[i + 1];
    }
    printf("%.2lf\n", g[0]);
    return ;
}

https://codeforces.com/gym/102861/problem/A
每次花费1可以得到 \([a, b]\) 范围的卡牌,问你拿到 \(n\) 张的期望花费
考虑倒推,\(f[n] = 0, f[i] = \frac {\sum_{j = a}^b f[i + j] + 1} {b - a + 1}\)
特判一下\(a = 0\) 的情况,要移一下项。

double f[maxn];
void run() {
    int n, a, b;
    scanf("%d %d %d", &n, &a, &b);
    double s = 0;   int l = (a == 0 ? 1 : a);
    for(int i = n - 1; i >= 0; -- i) {
        if(a)   f[i] = s / (b - a + 1) + 1;
        else    f[i] = (s + b + 1) / b;
        if(i + l - 1 < n)   s += f[i + l - 1];
        if(i + b < n)       s -= f[i + b];
    }
    printf("%.10lf\n", f[0]);
    return ;
}

https://www.luogu.com.cn/problem/P4316
就是每个点的期望都是下一个点的期望推过来的\(f[i] = \sum \frac {f[to[i]]} {sz[i]}\)
因为是无环图,所以考虑拓扑排序递推贡献就行了

int head[maxn], to[maxn], nxt[maxn], val[maxn], ecnt;
void add(int u, int v, int w) {
    to[++ecnt] = v; nxt[ecnt] = head[u]; head[u] = ecnt; val[ecnt] = w;
}

double f[maxn];
int sz[maxn], in[maxn];
void run() {
    int n, m; scanf("%d %d", &n, &m);
    for(int i = 1; i <= m; ++ i) {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        add(v, u, w);   in[u] ++;   sz[u] ++;
    }
    queue<int> q;
    q.push(n);
    while(!q.empty()) {
        int top = q.front();
        q.pop();
        for(int i = head[top]; i; i = nxt[i]) {
            f[to[i]] += (f[top] + val[i]) / sz[to[i]];
            in[to[i]] --;
            if(in[to[i]] == 0)  q.push(to[i]);
        }
    }
    printf("%.2lf\n", f[1]);
    return ;
}

https://vjudge.net/problem/CodeForces-518D
//当t小于等于n,就是t*p
//当t大于n,那么就会有的情况拿满了,不能继续贡献了
//考虑二维递推式,f[i][j]->第i秒有j个人,f[0][0] = 1,表示0的时候期望是1
//f[i][j] = f[i - 1][j - 1] * p + f[i - 1][j] * (j == n ? 1 : (1 - p)),n个人了就不变了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 2e3 + 10;
constexpr int MOD = 998244353;

double f[maxn][maxn];
void run() {
    int n, t;   double p;
    scanf("%d %lf %d", &n, &p, &t);
    f[0][0] = 1;
    for(int i = 1; i <= t; ++ i) {
        f[i][0] = f[i - 1][0] * (1 - p);
        for(int j = 1; j <= n && j <= i; ++ j)
            f[i][j] = f[i - 1][j - 1] * p + f[i - 1][j] * (j == n ? 1 : (1 - p));
    }
    double ans = 0;
    for(int i = 1; i <= n && i <= t; ++ i)    ans += f[t][i] * i;
    printf("%.10lf\n", ans);
    return ;
}

signed main() {
    int t = 1;
    while(t--)
    run();
    return 0;
}

https://www.luogu.com.cn/problem/P6154
其实就是求所有边的边长和,除以边数+点数

int head[maxn], to[maxn * 10], nxt[maxn * 10], ecnt;
void add(int u, int v) {
    to[++ecnt] = v; nxt[ecnt] = head[u]; head[u] = ecnt;
}

int ksm(int a, int b) {
    int res = 1;
    while(b) {
        if(b & 1)   res = 1ll * res * a % mod;
        a = 1ll * a * a % mod;
        b >>= 1;
    }
    return res;
}
int inv(int x) {
    if(x >= mod)    x -= mod;
    return ksm(x, mod - 2);
}

int sz[maxn], in[maxn], num[maxn], val[maxn], sum[maxn];
inline int qmod(int x) {
    if(x >= mod)    x -= mod;
    return x;
}
void run() {
    int n, m;
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= m; ++ i) {
        int u, v;   scanf("%d %d", &u, &v);
        add(v, u);  in[u] ++;
    }
    queue<int> q;
    for(int i = 1; i <= n; ++ i)    if(in[i] == 0)  q.push(i);
    for(int i = 1; i <= n; ++ i)    sz[i] = 1;

    int ans1 = 0, ans2 = 0;
    while(!q.empty()) {
        int top = q.front();
        q.pop();
        ans1 += sum[top];
        if(ans1 >= mod) ans1 -= mod;
        ans2 += sz[top];
        cout << top << " " << sz[top] << " " << endl;
        if(ans2 >= mod) ans2 -= mod;
        for(int i = head[top]; i; i = nxt[i]) {
            sz[to[i]] += sz[top];//树的点数
            if(sz[to[i]] >= mod)    sz[to[i]] -= mod;
            sum[to[i]] += qmod(sum[top] + sz[top] * 1/*val[i]*/);//树的连根路径和
            if(sum[to[i]] >= mod)   sum[to[i]] -= mod;
            in[to[i]] --;
            if(in[to[i]] == 0)  q.push(to[i]);
        }
        /*for(int i = head[top]; i; i = nxt[i]) {
            sz[to[i]] += sz[top];//树的所有点数
            val[to[i]] += val[top] + sz[top];//树的所有边数
            //num[to[i]] += num[top] + sz[top] * /val[i]/;//直接连边总长
            sum[to[i]] += sum[top] + num[top] + sz[top] * /val[i]/;//树的所有路径和
            in[to[i]] --;
            if(in[to[i]] == 0)  q.push(to[i]);
        }*/
    }
    //a * inv(b) = a % mod * inv(b % mod)
    printf("%d\n", 1ll * ans1 * inv(ans2) % mod);
    return ;
}

posted @ 2021-08-11 21:35  wlhp  阅读(14)  评论(0)    收藏  举报