【noi2019集训题1】 脑部进食 期望dp+高斯消元

题目大意:有n个点,m条有向边,每条边上有一个小写字母。

有一个人从1号点开始在这个图上随机游走,游走过程中他会按顺序记录下走过的边上的字符。

如果在某个时刻,他记录下的字符串中,存在一个子序列和S2相同,或者存在一个子串和S1相同,那么他就会当场去世。

他想知道他会不会当场去世,如果会,他想问你当场去世的时间的期望。

数据范围:n≤20,|S1|≤10,|S2|≤50

 

我们考虑列一个dp方程出来

设f[i][j][k]表示这人从1号点出发,当前走到i号点,且子串覆盖了S1的前j位,覆盖了S2的前k位的期望步数

然后你会发现你做不出来,因为最终根本无法统计答案(然后你就进死胡同了)

我们尝试把整个方程反过来

设$f[i][j][k]$表示你从$i$号点出发,之前走的路已经覆盖了$S1$的前$j$位,$S2$串的前$k$位的情况下,期望走多少部后会去世。

最终需要求的答案显然是$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 }

 

posted @ 2019-06-13 16:19  AlphaInf  阅读(431)  评论(0编辑  收藏  举报