【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 }