javascript
BZOJ 1444 [JSOI2009]有趣的游戏 (AC自动机、概率与期望DP、矩阵乘法)
誒這題洛谷居然沒有???
題目鏈接: https://www.lydsy.com/JudgeOnline/problem.php?id=1444
題解: 我見到主要有兩種做法。
一是矩陣乘法。設(shè)\(dp[t][i]\)表示時間\(t\)之后在AC自動機\(i\)節(jié)點的概率,那么轉(zhuǎn)移是一個矩陣乘法的形式,構(gòu)造轉(zhuǎn)移矩陣\(f\), 如果\(u\)是某個串的結(jié)尾點,則\(f[u][u]=1,f[u][v]=0 (v\ne u)\), 否則直接按概率搞。
然后這個矩陣的\(t\)次冪就可以得到\(t\)時刻后在每個點的概率。但是這題時間到無窮。
可以感覺到隨著\(t\)的增加不匹配任何串的概率不斷減小,\(t\)無窮大時達到0. 而題目精度要求并不高,所以如果我們能夠使得\(t\)非常大而精度誤差被控制在要求范圍內(nèi),就做完了。
非常粗略地估算一下,\(t=10^{12}\)時矩陣每一項的精度誤差遠小于\(1\times 10^{-4}\) (別問我怎么算的,我放縮的幅度太大了,實際的界應(yīng)該小于\(10^{12}\))
所以把矩陣自乘\(40\)次就完了。時間復雜度\(O(n^6\times 40)\) (所有范圍為\(10\)的參數(shù)不加以區(qū)分)
二是高斯消元。
對于任何節(jié)點\(u\), 其概率等于所有入邊的概率乘上其字母權(quán)值之和,然后這樣可以列出\(siz\) (AC自動機大小)個方程,但是如果這么解的話得到的答案是所有未知數(shù)全等于\(0\), 然后就把根的方程去掉,改成所有結(jié)束節(jié)點的概率之和等于\(1\), 解出來就A掉了……
完全不理解他是要干什么,拿某些題解代碼一看,發(fā)現(xiàn)有的點解出來概率都是\(2\)……
到最后終于翻到一篇詳細解釋這個做法的題解: 如果設(shè)概率直接拿方程列上會全得\(0\), 原來這個未知數(shù)設(shè)的并非概率,而是經(jīng)過該點的期望次數(shù)(對于結(jié)束節(jié)點期望次數(shù)就等于概率),然后\(x_0\) (\(0\)為根)等于所有入邊概率之和\(+1\) (因為上來先到一次), 其余節(jié)點等于所有入邊概率之和即可解。也可以不要根的那個等式,換成所有結(jié)束節(jié)點期望次數(shù)之和為\(1\), 應(yīng)該是等價的。(天哪看了一晚上終于看懂了……)
時間復雜度\(O(n^6)\)
據(jù)說還有一個枚舉每個人的\(O(n^7)\)做法……大佬們太強了我啥都看不懂啊
三是矩陣求逆,這個貌似(對我來說)還好理解一些……
矩陣第\(i\)行第\(j\)列是從\(i\)轉(zhuǎn)移到\(j\)的概率,結(jié)尾節(jié)點的這一行全部為0. 這個和做法一的區(qū)別就是結(jié)尾節(jié)點轉(zhuǎn)移到自己的概率不是\(1\), 那么原來要求\(T^{+\inf}\)現(xiàn)在就變成要求\(T+T^2+T^3+...+T^{+\inf}\), 這個矩陣元素都小于\(1\)應(yīng)該收斂的,那么直接求\(T\times (1-T)^{-1}\)即可。
時間復雜度\(O(n^6)\)
代碼
做法一
#include<cstdio> #include<cstdlib> #include<cstring> using namespace std;const int N = 10; const int S = 10; const int LEN = 10; const int SIZ = 100; const int LGM = 40; int son[SIZ+3][S+3]; int fail[SIZ+3]; int que[SIZ+3]; double p[N+3]; int id[N+3]; char a[N+3][LEN+3]; int n,len,s,siz;struct Matrix {double a[SIZ+3][SIZ+3];Matrix operator *(const Matrix &arg) const{Matrix ret;for(int i=0; i<=siz; i++) for(int j=0; j<=siz; j++) ret.a[i][j] = 0.0;for(int i=0; i<=siz; i++){for(int k=0; k<=siz; k++){for(int j=0; j<=siz; j++){ret.a[i][j] += a[i][k]*arg.a[k][j];}}}return ret;} } f;void insertstr(char str[],int sid) {int u = 0;for(int i=1; i<=len; i++){if(son[u][str[i]]==0) {siz++; son[u][str[i]] = siz;}u = son[u][str[i]];}id[sid] = u; }void buildACA() {int head = 1,tail = 0;for(int i=1; i<=s; i++){if(son[0][i]) {tail++; que[tail] = son[0][i];}fail[son[0][i]] = 0;}while(head<=tail){int u = que[head]; head++;for(int i=1; i<=s; i++){if(son[u][i]) {fail[son[u][i]] = son[fail[u]][i]; tail++; que[tail] = son[u][i];}else {son[u][i] = son[fail[u]][i];}}} }int main() {scanf("%d%d%d",&n,&len,&s);for(int i=1; i<=s; i++){double x,y; scanf("%lf%lf",&x,&y); p[i] = x/y;}for(int i=1; i<=n; i++){scanf("%s",a[i]+1); for(int j=1; j<=len; j++) a[i][j]-=64;insertstr(a[i],i);}buildACA();for(int u=0; u<=siz; u++){for(int i=1; i<=s; i++){int v = son[u][i];f.a[u][v] += p[i];}}for(int i=1; i<=n; i++){int u = id[i];for(int j=0; j<=siz; j++) f.a[u][j] = (u==j) ? 1.0 : 0.0;}for(int i=1; i<=LGM; i++){f = f*f;}for(int i=1; i<=n; i++) printf("%.2lf\n",f.a[0][id[i]]);return 0; }總結(jié)
以上是生活随笔為你收集整理的BZOJ 1444 [JSOI2009]有趣的游戏 (AC自动机、概率与期望DP、矩阵乘法)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Codeforces 432D Pref
- 下一篇: BZOJ 4327 [JSOI2012]