【noi2019集訓題1】 腦部進食 指望dp+高斯消元

題目大意:有n個點,m條有向邊,每條邊上有一個小寫字母。c++

有一我的從1號點開始在這個圖上隨機遊走,遊走過程當中他會按順序記錄下走過的邊上的字符。spa

若是在某個時刻,他記錄下的字符串中,存在一個子序列和S2相同,或者存在一個子串和S1相同,那麼他就會當場去世。code

他想知道他會不會當場去世,若是會,他想問你當場去世的時間的指望。blog

數據範圍:n≤20,|S1|≤10,|S2|≤50字符串

 

咱們考慮列一個dp方程出來it

設f[i][j][k]表示這人從1號點出發,當前走到i號點,且子串覆蓋了S1的前j位,覆蓋了S2的前k位的指望步數class

而後你會發現你作不出來,由於最終根本沒法統計答案(而後你就進死衚衕了)im

咱們嘗試把整個方程反過來統計

設$f[i][j][k]$表示你從$i$號點出發,以前走的路已經覆蓋了$S1$的前$j$位,$S2$串的前$k$位的狀況下,指望走多少部後會去世。nw

最終須要求的答案顯然是$f[1][0][0]$

咱們不難列出$f[i][j][k]=1+\frac{1}{d[i]} \sum\limits_{(i,u)∈E}f[u][j'][k']$

其中$d[i]$表示$i$號點的出度,$E$表示邊集,$j'$和$k'$的具體值視該轉移邊的字母而訂,很是好求。

這個方程咱們顯然能夠經過高斯消元求解,時間複雜度$O(n^3|S1|^3|S2|^3)$,愉快$TLE$

無解的狀況出現即爲某一條方程出現了被零除。

 

咱們發現,從$f[i][j'][k']$向$f[i][j][k]$轉移的過程當中,有$k'≥k$

那麼咱們顯然能夠固定$k$,對每個$k$作一次高斯消元,而後向下一層傳值,再高斯消元便可。

時間複雜度因而就下降到$O(n^3|S1|^3|S2|)$了

看起來有$4$個億

實際上因不明緣由,每一層須要轉移的節點數根本就去不到理論上屆

因此只跑了不到$20ms$(大霧)

 1 #include<bits/stdc++.h>
 2 #define M 205
 3 #define eps 1e-6
 4 using namespace std;
 5 
 6 double f[M][M]={0},ans[M]={0};
 7 
 8 void gauss(int n){
 9     for(int i=1;i<=n;i++){
10         int id=i;
11         for(int j=i;j<=n;j++) if(f[id][i]<f[j][i]) id=j;
12         swap(f[i],f[id]);
13         if(fabs(f[i][i])<eps) {printf("-1\n"); exit(0);}
14         for(int j=i+1;j<=n;j++){
15             double cha=f[j][i]/f[i][i];
16             for(int k=i;k<=n+1;k++) f[j][k]-=f[i][k]*cha;
17         }
18     }
19     for(int i=n;i;i--){
20         for(int j=i+1;j<=n;j++) f[i][n+1]-=ans[j]*f[i][j];
21         ans[i]=f[i][n+1]/f[i][i];
22     }
23 }
24 
25 struct edge{int u,next;char v;}e[600]={0}; int head[M]={0},use=0;
26 void add(int x,int y,char z){use++;e[use].u=y;e[use].v=z;e[use].next=head[x];head[x]=use;}
27 int n,m;
28 
29 char w[M]={0},p[M]={0}; int lenw,lenp;
30 int nxt[M]={0};
31 
32 void upd(char C,int id,int &nowj,int &nowk){
33     for(;nowj&&w[nowj+1]!=C;nowj=nxt[nowj]);
34     if(w[nowj+1]==C) nowj++;
35     if(p[nowk+1]==C) nowk++;
36 }
37 
38 int vis[22][11][55]={0};
39 void dfs(int i,int j,int k){
40     if(vis[i][j][k]) return;
41     vis[i][j][k]=1;
42     if(j==lenw||k==lenp) return;
43     for(int l=head[i];l;l=e[l].next){
44         char C=e[l].v; int id=e[l].u;
45         int nowj=j,nowk=k;
46         upd(C,id,nowj,nowk);
47         dfs(id,nowj,nowk);
48     }
49 }
50 
51 int id[22][11]={0},iid[22][11]={0},cnt=0; double d[M]={0};
52 
53 int main(){
54     scanf("%d%d",&n,&m);
55     for(int i=1;i<=m;i++){
56         int x,y; char z[10]; scanf("%d%d%s",&x,&y,z);
57         add(x,y,z[0]); d[x]++;
58     }
59     scanf("%s",w+1); lenw=strlen(w+1);
60     scanf("%s",p+1); lenp=strlen(p+1);
61     for(int i=2,j=0;i<=n;i++){
62         for(;j&&w[j+1]!=w[i];j=nxt[j]);
63         if(w[j+1]==w[i]) j++;
64         nxt[i]=j;
65     }
66     dfs(1,0,0);
67     for(int k=lenp-1;~k;k--){
68         int cnt=0;
69         for(int i=1;i<=n;i++)
70         for(int j=0;j<lenw;j++){
71             id[i][j]=0;
72             if(vis[i][j][k]) 
73             id[i][j]=++cnt;
74         }
75         memset(f,0,sizeof(f));
76         for(int i=1;i<=n;i++)
77         for(int j=0;j<lenw;j++) if(id[i][j]){
78             int ID=id[i][j];
79             f[ID][ID]=d[i]; 
80             f[ID][cnt+1]=d[i];
81             for(int l=head[i];l;l=e[l].next){
82                 char C=e[l].v; int u=e[l].u;
83                 int nowj=j,nowk=k;
84                 upd(C,u,nowj,nowk);
85                 if(nowj==lenw) continue;
86                 if(nowk==k){
87                     f[ID][id[u][nowj]]--;
88                 }else{
89                     f[ID][cnt+1]+=ans[iid[u][nowj]];
90                 }
91             }
92         }
93         memcpy(iid,id,sizeof(id));
94         memset(ans,0,sizeof(ans));
95         gauss(cnt);
96     }
97     printf("%.10lf\n",ans[1]);
98 }
相關文章
相關標籤/搜索