概率期望學習筆記


 本文大部分來源於胡淵鳴學長的2013年國家集訓隊論文《淺談信息學競賽中概率論的基礎與應用》

從理論的角度?來學習概率......但由於本人實力比較弱,歡迎指正

部分公式的表達可能不太嚴謹......能理解就行

歡迎轉載,只是需要注明出處....

 

1.概率

我們定義這么一個函數,

函數$P(A)$,表示 事件$A$ 發生的可能性的大小,稱作概率測度

此時,$A$是事件集合$F$的一個子集,注意事件本身也有可能是一個集合

並且所有的事件$A$都可以看做是樣本空間$\Omega$的一個子集

 

那么,合法的三元組$(\Omega, F, P)$為就可以被稱為概率空間

這里,合法的定義為:

$$\forall x \in \Omega, P(x) \geq 0$$

$$P(\Omega) = 1$$

$$A \cap B = \varnothing \to P(A \cup B) = P(A) + P(B)$$

 

$\Omega$是全集

$F$是囊括了所有的事件

$P$是一種把集合映射到實數上的函數,其意義為事件發生的可能性大小

 

$P$比較好理解

那$F$和$\Omega$的區別在哪里呢?

比如拋硬幣

全集$\Omega$為{ 正面向上,反面向上,立起 };

記正面向上為$A$, 反面向上為$B$,立起為$C$

那么$F$應為$\{ \{A, B, C\}, \{A, B\}, \{A, C\}, \{B, C\}, \{A\}, \{B\}, \{C\}, \varnothing \}$

也就是說,全集類似於所有最小單位的集合,而$F$是全集中的元素組合出的集合

 

2.條件概率

在我們知道了一些黑暗的事情后,事件的概率是會改變的

比如在街上隨便找個人,他是男生的概率近似於$0.5$

但是,如果你知道他學$OI$,那么他是男生的概率可以提升至$0.999999$

 

條件概率的公式為

$$P(A | B) = \frac{P(A \cap B)}{P(B)}$$

 

實際上,條件概率是變換樣本空間的一種思考方式

比如上面的例子,隨便找個人,那么樣本空間是全球的人

但是如果他學$OI$,那么我們考慮的人就只是學$OI$中的人

可以認為,條件概率表示了兩個樣本空間之間概率測度的關系

 

也就是用一個實數來表示 (你是個普通人,你要$ak$) 和(你是神仙,你要$ak$)這種現實差的可能性...

大概?

 

3.全概率公式

如果$B_1, B_2 ...... B_n$是樣本空間的一個划分,那么有

$$P(A) = \sum\limits_{i} P(A | B_i) * P(B_i)$$

 

好比,你要$ak$的概率可以拆分成

(你是女生,你要$ak$),(你是男生,你要$ak$),

(你是人妖,你要$ak$),(你無性別,你要$ak$),

四部分的概率,而通過條件概率,我們就能通過每部分的概率得出總概率

 

注意划分的意義:

$\forall B_i, B_j \in \Omega,B_i \cap B_j = \varnothing$

$\bigcup B_i = \Omega$

 

4.貝葉斯公式

$$P(A|B) = \frac{P(B|A)P(A)}{P(B)}$$

轉化成$P(A \cap B)$就能證明了...

 

可以有效的計算逆向概率

其實體現了兩個不同樣本空間對同一樣本空間的關系的關系(有點繞)...

 

其中,$P(A), P(B)$可以用全概率公式展開

但是太鬼畜了,公式難打,就不展開了

 

5.隨機變量

隨機變量一點都不有趣,它既不隨機,也不是一個變量

隨機變量是定義在$\Omega$上的一個函數

也就是函數$f : \Omega \to R$是一個隨機變量

 

通過隨機變量,我們可以通過考慮值來考慮事件

可以認為這是對$\Omega$的一種划分,把$f(X)$相同的$X$划分到了一起的一種方法.....

 

對於一個$\Omega$,隨機變量有很多個

但是,我們一般都默認是權值......也有認為是次數的時候......

 

6.期望

定義隨機變量$X$的期望為

$$E[X] = \sum\limits_{x} P(x) X(x) = \sum\limits_{x} xP(X = x)$$

其中,$P(X= x)$表示$\sum\limits_{y} P(y)[X(y) = x]$

期望可以理解為加權平均

 

這里本人有一個奇怪的疑惑?

在做期望$dp$的時候,轉移中根本沒有體現隨機變量這種函數樣的東西....

所以期望到底代表了什么?

 

於是從百度百科拿了個例子

城市中有$0, 1, 2, 3$個孩子的家庭的概率分別為$0.01,0.9,0.06,0.03$

那么我們定義隨機變量$X$表示城市中一個家庭中孩子的數量

(這里的$X$大概是這個意思:比如$X(a) = 1$表示$a$號家庭中有1個孩子,$X(b) = 2$表示$b$號家庭中有2個兒子)

那么,根據公式有$E[X] = 1.11$,表示城市中一個家庭孩子的平均數量

也就是說,期望體現了所有取值下函數(隨機變量)$X$的一個平均值

(跟積分很像是不是...)

 

我們考慮這么一類隨機變量$X[S](x)$,表示某個集合中$x$元素達到滿足$S$條件的代價

那么,$E[X[S]]$就表示某個集合達到滿足$S$條件的平均代價

同樣的,我們還可以考慮構造$E[X[S][T]]$表示某個集合從滿足$S$條件達到滿足$T$條件的平均代價

$S$條件甚至可以是限制為某個集合的子集這種.......神奇......

可以發現,實際上它才是我們平時$dp$的真面目......

 

根據期望的公式,我們有兩個計算方式:

1.考慮枚舉每一個事物,計算其概率再求期望

2.考慮枚舉權值$x$,計算權值的概率求期望

 

7.獨立事件

如果兩個事件$A, B$,滿足$P(A \cap B) = P(A) * P(B)$

那么,我們就稱$A, B$為獨立事件

 

當然,有時我們可以通過人腦智慧判斷$A, B$獨不獨立($A$不影響$B$,$B$不影響$A$)后,

使用$P(A \cap B) = P(A) * P(B)$這條定義......(定義當性質用真的好嗎?)

 

8.期望的相關運算

如果你英文和數學足夠好

請參見神奇的條目

有些證明參照上面的鏈接

特殊的期望

$$\forall x \in \Omega, X(x) = c \to E[X] = c$$

期望的線性性質

$$E[aX + bY] = aE[X] + bE[Y]$$

期望的積

兩個隨機變量$X, Y$

滿足$\forall x_1 \in D_X,y_1 \in D_Y,P(X = x_1 \cap Y = y_1) = P(X = x_1) P(Y = y_1)$

則稱它們為獨立的隨機變量

那么,它們滿足

$$E[XY] = E[X]E[Y]$$

全期望公式

$$E[E[X | Y]] = E[X]$$ 

 

拒絕紙上談兵......

上菜............

 

一些可食用的題目

首先我們來板刷洛谷吧... 

 

luoguP1850 換教室

 

 

令$dp[i][j][0/1]$表示到了第$i$天,換了$j$次教師,第$i -1$天在原來的教室還是換了的最小期望

注意轉移的時候考慮完整,不要漏情況了

代碼是以前的,風格自己也無法掌控...

 

#include <cstdio>
#define INF 1047483647
#define ri register int
#define dl double
using namespace std;

inline dl min(dl a, dl b) {
    return (a < b) ? a : b;
}

inline int imin(int a, int b) {
    int c = (a - b) >> 31;
    return a & c | b & ~c;
}

int n, m, v, e;
int c[2005], d[2005];
int dis[305][305];
dl dp[2005][2005][2];
dl k[2005], dk[2005];
dl ans = INF;

inline void Init() {
    for(ri i = 1; i <= v; i ++)
    for(ri j = i + 1; j <= v; j ++)
    dis[i][j] = dis[j][i] = INF;
    for(ri i = 1; i <= n; i ++)
    for(ri j = 0; j <= m; j ++)
    dp[i][j][0] = dp[i][j][1] = INF;
    dp[1][0][0] = dp[1][1][1] = 0;
}

inline void In() {
    int u, vv, w;
    scanf("%d%d", &n, &m);
    scanf("%d%d", &v, &e);
    for(ri i = 1; i <= n; i ++) scanf("%d", &c[i]);
    for(ri j = 1; j <= n; j ++) scanf("%d", &d[j]);
    for(ri i = 1; i <= n; i ++)
    scanf("%lf", &k[i]), dk[i] = 1.0 - k[i];
    Init();
    for(ri i = 1; i <= e; i ++) {
        scanf("%d%d%d", &u, &vv, &w);
        dis[u][vv] = dis[vv][u] = imin(w, dis[u][vv]);
    }
}

inline void Floyd() {
    for(ri l = 1; l <= v; l ++)
    for(ri i = 1; i <= v; i ++)
    for(ri j = 1; j <= v; j ++)
    dis[i][j] = imin(dis[i][j], dis[i][l] + dis[l][j]);
}

inline void DP() {
    for(ri i = 2; i <= n; i ++) {
        dp[i][0][0] = dp[i - 1][0][0] + dis[c[i - 1]][c[i]];
        int tmp1 = dis[c[i - 1]][c[i]];
        int tmp2 = dis[d[i - 1]][c[i]];
        int tmp3 = dis[d[i - 1]][d[i]];
        int tmp4 = dis[c[i - 1]][d[i]];
        for(ri j = 1; j <= m; j ++) {
            dp[i][j][0] = min(dp[i - 1][j][0] + tmp1, dp[i - 1][j][1] + tmp1 * dk[i - 1] + tmp2 * k[i - 1]);
            //嗯。。。。。。
            dp[i][j][1] = min(dp[i - 1][j - 1][0] + tmp1 * dk[i] + tmp4 * k[i], dp[i - 1][j - 1][1] + tmp1 * dk[i - 1] * dk[i] + tmp2 * k[i - 1] * dk[i] + tmp4 * dk[i - 1] * k[i] + tmp3 * k[i - 1] * k[i]);
        }
    }
}

inline void Get_ans() {
    for(ri i = 1; i <= m; i ++)
    ans = min(ans, min(dp[n][i][0], dp[n][i][1]));
    ans = min(ans, dp[n][0][0]);
    printf("%.2lf", ans);
}

inline void Work() {
    Floyd();
    DP();
    Get_ans();
}

int main() {
    In();
    Work();
    return 0;
}
1

 

luoguP3802 小魔女帕琪

小魔女帕琪到底是誰啊?蕾咪那么可愛,為什么要打啊....

然后....不懂題解在寫什么東西....

這題其實還是有點意思的

首先考慮通過枚舉來求期望

發現權值都是統一的1,所以考慮求出概率和就行了

可以發現,揮霍完所有的能量晶體對應一個長度為$S = \sum\limits_{i = 1}^7 a[i]$的排列

那么,我們需要枚舉這個連續$7$個元素出現的位置,自然有$S - 6$個不同的位置

接着,我們選中這$7$個元素,有$\prod\limits_{i = 1}^7 a[i]$種選法

這$7$個元素在指定的位置任意排列,剩余的$S - 7$個元素也任意排列

並且總方案數為$S!$,且概率相同,那么最終概率和為$$\frac{(S - 6) * (S - 7)! * 7! * \prod\limits_{i =1}^7 a[i]}{S!}$$

注:輸出$0.000$是什么鬼,大部分人的程序好像都是錯的,$0\;0\;1\;1\;0\;0\;0$就不行了......唉.....

#include <cstdio>
#include <iostream>
using namespace std;

#define de long double

de ans = 5040;
de a[20], S;

int main() {
    for(int i = 1; i <= 7; i ++) {
        cin >> a[i]; S += a[i];
        if(a[i] == 0) { printf("0.000\n"); return 0; } 
    }
    for(int i = 1; i <= 7; i ++) ans = ans * a[i] / (S - i + 1);
    printf("%.3Lf\n", ans * (S - 6));
    return 0;
}
2

 

 

luoguP4316 綠豆蛙的歸宿

 

兩種方法

第一種是利用期望的線性性質,總長度期望 = 所有的邊的期望和,那么弄出每一條邊的概率即可

#include <cstdio>
#include <cstring>
using namespace std;

extern inline char gc() {
    static char RR[23456], *S = RR + 23333, *T = RR + 23333;
    if(S == T) fread(RR, 1, 23333, stdin), S = RR;
    return *S ++;
}
inline int read() {
    int p = 0, w = 1; char c = gc();
    while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
    while(c >= '0' && c <= '9')  p = p * 10 + c - '0', c = gc();
    return p * w;
}

#define de double
#define ri register int
#define sid 200050

de f[sid], ans;
int n, m, cnp, du[sid], in[sid], cd[sid], q[sid];
int cap[sid], nxt[sid], node[sid], fee[sid];

inline void addeg(int u, int v, int w) {
    nxt[++ cnp] = cap[u]; cap[u] = cnp;
    node[cnp] = v; fee[cnp] = w;
}

int main() {
    n = read(); m = read();
    for(ri i = 1; i <= m; i ++) {
        int u = read(), v = read(), w = read();
        addeg(u, v, w); du[v] ++; in[v] ++; cd[u] ++;
    }
    int fr = 1, to = 0;
    q[++ to] = 1; f[1] = 1; 
    while(fr <= to) {
        int o = q[fr ++];
        for(ri i = cap[o]; i; i = nxt[i]) {
            int d = node[i];
            f[d] += f[o] / (de)(cd[o]);
            ans += fee[i] * f[o] / (de)(cd[o]);
            in[d] --; if(!in[d]) q[++ to] = d;
        }
    }
    printf("%.2lf\n", ans);
    return 0;
}
3

第二種方法是直接在拓撲上$dp$

由於是期望$dp$,那么需要逆推......

至於為什么?因為從點$x$必須要走到$n$,但是從點$1$不一定會走到$x$

並且還可以排除很多合法的情況

何樂而不為?代碼就......咕咕咕咕咕咕咕咕

 

luoguP1297 [國家集訓隊]單選錯位

 

挺簡單的......

由於權值是$1$,因此實際上只要求概率和

對於第$i$道題,做對它的要求為第$i$題和第$i - 1$題的答案一樣

因此,其概率為$\frac{min(a[i], a[i - 1])}{a[i] * a[i - 1]} = \frac{1}{max(a[i], a[i - 1])}$

#include <cstdio>
#include <iostream>
using namespace std;

#define de double
#define ri register int
#define mod 100000001

de ans;
int n, A, B, C;
int a, a1, b, c, d;

int main() {
    cin >> n >> A >> B >> C >> a;
    a1 = a % C + 1; d = a % C + 1;
    for(ri i = 2; i <= n; i ++) {
        c = (1ll * a * A % mod + B) % mod;
        b = c % C + 1;
        ans += 1.0 / (de)(max(d, b));
        a = c; d = b;
    }
    ans += 1.0 / (de)(max(b, a1));
    printf("%.3lf\n", ans);
    return 0;
}
4

 

luoguP1365 WJMZBMR打osu! / Easy

略有感觸......

設一段區間的長度為$n$,那么它對答案的貢獻為$n^2$,這不是一個線性的變換,我們想個辦法讓它變為線性的

只要變換是線性的,那么我們可以直接使用期望$dp$

考慮增量,$(n + 1)^2 - n^2 = 2 * n + 1$,發現增量是線性的

那么我們直接統計增量的影響,而不是直接統計區間的影響

令$f[i]$表示$dp$到了第$i$位的期望得分,$g[i]$表示$dp$到了$i$位的前置期望$o$長度

那么有

$$f[i] = f[i - 1] + g[i - 1] * 2 + 1, g[i] = g[i - 1] + 1 \;(s[i] == o)......(1)$$

$$f[i] = f[i - 1], g[i] = 0\;(s[i] == x)......(2)$$

$$f[i] = \frac{(1) + (2)}{2}, g[i] = \frac{(1) + (2)}{2}\;(s[i] == ?)$$

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

#define de double
#define ri register int
#define sid 2000050

int n;
char s[sid];
de f[sid], g[sid];

int main() {
    cin >> n; scanf("%s", s + 1);
    for(ri i = 1; i <= n; i ++) {
        if(s[i] != 'x') { f[i] += f[i - 1] + g[i - 1] * 2 + 1; g[i] += g[i - 1] + 1; }
        if(s[i] != 'o') f[i] += f[i - 1];
        if(s[i] == '?') f[i] /= 2.0, g[i] /= 2.0;
    }
    printf("%.4lf\n", f[n]);
    return 0;
}
5

 

luoguP3924 康娜的線段樹

既然題目名字叫線段樹,那我們就用線段樹來做.......好像只有我一個用線段樹卡過去了?

運用期望的線性性質,總期望可以拆分成線段樹中的每個節點的期望的和。

對於線段樹中的每個節點,我們維護這么幾個信息

$v:$,線段樹的這個節點及其子樹內的概率的權值和(具體看代碼吧,不方便用言語描述)

$p:$,線段樹的這個節點的概率

每次修改的時候,完整包含的區間直接調用$v$,否則計算出查詢區間與當前區間的交集$* p$的值

具體看代碼吧

復雜度$O(n \log n)$

注:$ans$要用$long\;double$存精度才夠...

注2:輸入和輸出優化不加你就$TTT$,雖然我沒有$T$過

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

extern inline char gc() {
    static char RR[23456], *S = RR + 23333, *T = RR + 23333;
    if(S == T) fread(RR, 1, 23333, stdin), S = RR;
    return *S ++;
}
inline int read() {
    int p = 0, w = 1; char c = gc();
    while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
    while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
    return p * w;
}

int wr[50], rw;
#define pc(z) *I ++ = z
char WR[20000005], *I = WR;
template <typename re>
inline void write(re x) {
    if(!x) pc('0');
    if(x < 0) x = -x, pc('-');
    while(x) wr[++ rw] = x % 10, x /= 10;
    while(rw) pc(wr[rw --] + '0'); pc('\n');
}

#define sid 1005000
#define ri register int
#define ll long long
#define de long double

int n, m, mmp;
int a[sid], s[sid * 4];
ll k[sid * 4], v[sid * 4];
de ans;

#define ls (o << 1)
#define rs (o << 1 | 1)

void build(int o, int l, int r, int p) {
    k[o] = p;
    if(l == r) {
        a[l] = read(); ans += (de)k[o] * a[l];
        v[o] = p; s[o] = a[l]; return;
    }
    int mid = (l + r) >> 1;
    build(ls, l, mid, p / 2);
    build(rs, mid + 1, r, p / 2);
    s[o] = s[ls] + s[rs]; ans += (de)k[o] * s[o];
    v[o] = v[ls] + v[rs] + k[o] * (r - l + 1);
}

ll query(int o, int l, int r, int ml, int mr) {
    if(ml > r || mr < l) return 0;
    if(ml <= l && mr >= r) return v[o];
    int mid = (l + r) >> 1; ll tmp = 0;
    if(ml >= l && mr <= r) tmp = k[o] * (mr - ml + 1);
    if(ml < l) tmp = k[o] * (mr - l + 1);
    if(mr > r) tmp = k[o] * (r - ml + 1);
    return query(ls, l, mid, ml, mr) + query(rs, mid + 1, r, ml, mr) + tmp;
}

int main() {
    n = read(); m = read(); mmp = read();
    int p = 1 << 22;
    build(1, 1, n, p);
    for(ri i = 1; i <= m; i ++) {
        int l = read(), r = read(), x = read();
        ans += (de)query(1, 1, n, l, r) * x;
        write((ll)((de)(ans / p) * mmp));
    }
    fwrite(WR, 1, I - WR, stdout);
    return 0;
}
6

 

luoguP2473 [SCOI2008]獎勵關

首先,我們注意到這涉及到最優策略的問題,由於這題不可貪心,因此我們需要把所有的決策所需的條件枚舉出來

也就是說,我們需要狀態壓縮來確定如何做決策,又由於期望逆推

設$f[i][S]$表示第$i$輪狀態為$S$,第$i + 1$至$k$輪的最大期望得分

枚舉寶物$j$

如果$j$能選,那么$f[i][S] += \frac{max(f[i + 1][S], val[j] + f[i + 1][S | 2^j])}{n}$

否則,$f[i][S] += \frac{f[i + 1][S]}{n}$

$f[1][0]$即為所求

事實上,逆推用$dfs$實現更為的合適...

#include <cstdio>
#include <iostream>
using namespace std;

#define sid 105
#define de double
#define ri register int

int n, k;
int bit[50];
de f[sid][130050];
int val[sid], pre[sid];

int main() {
    cin >> k >> n;
    for(ri i = 0; i <= 20; i ++) bit[i] = 1 << i;
    for(ri i = 1; i <= n; i ++) {
        cin >> val[i]; int w;
        while(1) {
            cin >> w; if(w == 0) break;
            pre[i] |= bit[w - 1];
        }
    }
    for(ri i = k; i; i --)
    for(ri j = 0; j <= bit[n] - 1; j ++) {
        for(ri o = 1; o <= n; o ++)
        if((j & pre[o]) == pre[o]) f[i][j] += max(f[i + 1][j], val[o] + f[i + 1][j | bit[o - 1]]);
        else f[i][j] += f[i + 1][j];
        f[i][j] /= (de)n;
    }
    printf("%.6lf\n", f[1][0]);
    return 0;
}
7

 

luoguP3412 倉鼠找sugar II 

倉鼠找sugar I是啥來着?忘了...

首先求總期望的話,用期望的線性性質拆分成每條邊的期望

對於一條邊$u - v$而言,$u \to v$和$v \to u$的期望是不同的

考慮分開統計,記$u \to v$的期望步數為$f[u \to v]$

那么有$f[u \to fa] = \frac{1}{du[u]} + \sum\limits_{v \in son_u} \frac{f[u \to fa] + f[v \to u] + 1}{du[u]}$

化簡后為$f[u \to fa] = du[u] + \sum\limits_{v \in son_u} f[v \to u]$

對於一個點而言,通過遞推式,我們發現,除了它走向父親的邊期望走過1次以外,其余邊恰好期望走過2次!

因此有$f[u \to fa] = 2 * sz[u] - 1$

同時對於邊$u \to v$,有$\frac{sz[u] *(n - sz[u])}{n^2}$的概率經過它,對於邊$v \to u$同理 

$O(n)$統計即可

至於有理數取模,把$n^2$提出來最后乘逆元就行

#include <cstdio>
using namespace std;

extern inline char gc() {
    static char RR[23456], *S = RR + 23333, *T = RR + 23333;
    if(S == T) fread(RR, 1, 23333, stdin), S = RR;
    return *S ++;
}
inline int read() {
    int p = 0, w = 1; char c = gc();
    while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
    while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
    return p * w;
}

#define mod 998244353
#define ll long long
#define ri register int
#define sid 200050

int n, cnp, ans;
int nxt[sid], node[sid], cap[sid], sz[sid];

inline void adeg(int u, int v) {
    nxt[++ cnp] = cap[u]; cap[u] = cnp; node[cnp] = v;
}

inline int fp(int a, int k) {
    int ret = 1;
    for( ; k; k >>= 1, a = 1ll * a * a % mod)
    if(k & 1) ret = 1ll * ret * a % mod;
    return ret;
}

#define cur node[i]
void dfs(int o, int fa) {
    sz[o] = 1;
    for(int i = cap[o]; i; i = nxt[i])
    if(cur != fa) dfs(cur, o), sz[o] += sz[cur];
    ans = (ans + 1ll * sz[o] * (n - sz[o]) % mod * (n * 2 - 2) % mod) % mod;
}

int main() {
    n = read();
    for(ri i = 1; i < n; i ++) {
        int u = read(), v = read();
        adeg(u, v); adeg(v, u);
    }
    dfs(1, 0);
    printf("%d\n", 1ll * ans * fp(1ll * n * n % mod, mod - 2) % mod);
    return 0;
}
8

 

luoguP2221 [HAOI2012]高速公路

$n \leqslant 10^5$

首先,使用期望的線性性質,整個區間的期望 = 每個節點的期望之和

那么,如果記$E[x]$為$x$點出現的期望

那么有$E[x] = v[x] * (x - l + 1) * (r - x + 1)$

現在要區間詢問,於是考慮求和$E[l, r] = \sum\limits_{i = l}^r E[i] = \sum\limits_{i = l}^r v[i] * (i - l + 1) * (r - i + 1)$ 

在線段樹中維護$v[i]*i^2, v[i]*i, v[i]$即可回答

代碼是以前寫的......畫風自適應吧.....復雜度$O(n \log n)$

#include <cstdio>
#define ri register int
#define ll long long
#define int long long
#define sid 2000500
using namespace std;

#define getchar() *S ++
char RR[30000005], *S = RR;
inline int read() {
    int p = 0, w = 1;
    char c = getchar();
    while(c > '9' || c < '0') { if(c == '-') w = -1; c = getchar(); }
    while(c >= '0' && c <= '9') { p = p * 10 + c - '0'; c = getchar(); }
    return p * w;
}

inline int Get_Opt() {
    char c = getchar();
    while(c > 'Z' || c < 'A') c = getchar();
    if(c == 'C') return 1;
    else return 2;
}

int n, m, ml, mr;
ll s1, s2, s3, mc;
ll p1[sid], p2[sid];
ll sum1[sid], sum2[sid], sum3[sid], lz[sid];

inline ll gcd(ll a, ll b) {
    return (b) ? gcd(b, a % b) : a;
}

inline void Update(int p) {
    sum1[p] = sum1[p << 1] + sum1[p << 1 | 1];
    sum2[p] = sum2[p << 1] + sum2[p << 1 | 1];
    sum3[p] = sum3[p << 1] + sum3[p << 1 | 1];
}

inline void Pushdown(int p, int l, int r) {
    if(!lz[p]) return;
    int ls = (p << 1), rs = (p << 1 | 1);
    int mid = (l + r) >> 1;
    lz[ls] += lz[p]; lz[rs] += lz[p];
    sum1[ls] += (mid - l + 1) * lz[p];
    sum1[rs] += (r - mid) * lz[p];
    sum2[ls] += (p1[mid] - p1[l - 1]) * lz[p];
    sum2[rs] += (p1[r] - p1[mid]) * lz[p];
    sum3[ls] += (p2[mid] - p2[l - 1]) * lz[p];
    sum3[rs] += (p2[r] - p2[mid]) * lz[p];
    lz[p] = 0;
}

inline void Modify(int p, int l, int r) {
    if(ml <= l && mr >= r) {
        sum1[p] += (r - l + 1) * mc;
        sum2[p] += (p1[r] - p1[l - 1]) * mc;
        sum3[p] += (p2[r] - p2[l - 1]) * mc;
        lz[p] += mc; return;
    }
    Pushdown(p, l, r);
    int mid = (l + r) >> 1;
    if(ml <= mid) Modify(p << 1, l, mid);
    if(mr > mid) Modify(p << 1 | 1, mid + 1, r);
    Update(p);
}

inline void Query(int p, int l, int r) {
    if(ml <= l && mr >= r) {
        s1 += sum1[p]; s2 += sum2[p];
        s3 += sum3[p]; return;
    }
    Pushdown(p, l, r);
    int mid = (l + r) >> 1;
    if(ml <= mid) Query(p << 1, l, mid);
    if(mr > mid) Query(p << 1 | 1, mid + 1, r);
    Update(p);
}

inline void Init() {
    for(ri i = 1; i <= n - 1; i ++) {
        p1[i] = i + p1[i - 1];
        p2[i] = i * i + p2[i - 1];
    }
}

signed main() {
    fread(RR, 1, sizeof(RR), stdin);
    n = read(); m = read();
    Init();
    for(ri i = 1; i <= m; i ++) {
        int opt = Get_Opt(), x = read(), y = read() - 1;
        if(x > y) {
            printf("0/0\n"); continue;
        }
        if(opt == 2) {
            ml = x; mr = y;
            s1 = s2 = s3 = 0;
            Query(1, 1, n);
            ll ans1 = (y - x + 1) * (y - x + 2) / 2;
            ll ans2 = -s3 + (x + y) * s2 + (y - x - y * x + 1) * s1;
            ll d = gcd(ans1, ans2);
            printf("%lld/%lld\n", ans2 / d, ans1 / d);
        }
        else {
            ml = x; mr = y; mc = read();
            Modify(1, 1, n);
        }
    }
    return 0;
}
9

 

luoguP3211 [HNOI2011]XOR和路徑

比較經典的高斯消元模型,求期望使用逆推

由於是異或和,因此按位考慮

$f[i][0 / 1]$表示$i$號節點走到$n$號節點的路徑$xor$和在第$i$位為$0 / 1$時的期望

注意到$f[i][0] + f[i][1] = 1$

因此$f[i][1] = \frac{1}{du[i]} * \sum\limits_{(i, v) = 1} f[v][0] + \sum\limits_{(i, v) = 0} f[v][1]$

$f[i][1] = \frac{1}{du[i]} * \sum\limits_{(i, v) = 1} (1 - f[v][1]) + \sum\limits_{(i,v) = 0} f[v][1]$

注意到$f$值之間互相轉移,因此使用高斯消元

$f[i][1] * du[i] + \sum\limits_{(i, v) = 1} f[v][1] - \sum\limits_{(i, v) = 0} f[v][1] = \sum\limits_{(i, v) = 1} 1$

注意$f[n][1] = 0$

#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;

extern inline char gc() {
    static char RR[23456], *S = RR + 23333, *T = RR + 23333;
    if(S == T) fread(RR, 1, 23333, stdin), S = RR;
    return *S ++;
}
inline int read() {
    int p = 0, w = 1; char c = gc();
    while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
    while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
    return p * w;
}

#define ri register int
#define de double
#define sid 205
#define eid 30005

int n, m, cnp, bit[50];
int du[sid], cap[sid], node[eid], nxt[eid], fee[eid];

inline void addedge(int u, int v, int w) {
    du[u] ++;
    nxt[++ cnp] = cap[u]; cap[u] = cnp; 
    node[cnp] = v; fee[cnp] = w;
}

de ans, f[sid][sid];
#define cur node[i]

void Guass() {
    for(ri i = 1; i <= n; i ++) {
        int p = i;
        for(ri j = i; j <= n; j ++)
        if(fabs(f[p][i]) < fabs(f[j][i])) p = j; 
        swap(f[i], f[p]);
        for(ri j = i + 1; j <= n; j ++) {
            de t = f[j][i] / f[i][i];
            for(ri k = i; k <= n + 1; k ++) f[j][k] -= t * f[i][k];
        }
    }
    for(ri i = n; i >= 1; i --) {
        f[i][n + 1] = f[i][n + 1] / f[i][i];
        for(ri j = i - 1; j >= 1; j --)
        f[j][n + 1] -= f[i][n + 1] * f[j][i];
    }
}

int main() {
    n = read(); m = read();
    for(ri i = 1; i <= m; i ++) {
        int u = read(), v = read(), w = read();
        if(u == v) addedge(u, v, w);
        else addedge(u, v, w), addedge(v, u, w);
    }
    for(ri I = 0; I <= 30; I ++) {
        bit[I] = 1 << I; f[n][n] = 1;
        for(ri j = 1; j < n; j ++) {
            f[j][j] = du[j];
            for(ri i = cap[j]; i; i = nxt[i])
            if(fee[i] & bit[I]) f[j][cur] ++, f[j][n + 1] ++;
            else f[j][cur] --;
        }
        Guass();
        ans += bit[I] * f[1][n + 1];
        for(ri i = 1; i <= n; i ++)
        for(ri j = 1; j <= n + 1; j ++)
        f[i][j] = 0;
    }
    printf("%.3lf\n", ans);
    return 0;
}
10

 

為了更好的閱讀體驗,更好的騙訪問量,之后的就單篇單篇的發吧

去數學 / 動態規划隨便翻翻應該就有了...如果我勤快的話


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM