1. 程式人生 > >【BZOJ 2553】[BeiJing2011]禁忌 AC自動機+期望概率dp

【BZOJ 2553】[BeiJing2011]禁忌 AC自動機+期望概率dp

我們 string cst def main 乘法 i++ jin 自動

我一開始想的是倒著來,發現太屎,後來想到了一種神奇的方法——我們帶著一個既有期望又有概率的矩陣,偶數(2*id)代表期望,奇數(2*id+1)代表概率,初始答案矩陣一列,1的位置為1(起點為0),工具矩陣上如果是直接轉移那麽就是由i到j概率期望都乘上1/alphabet,特別的,對於一個包含禁忌串的節點直接由其父節點指向0,而且在計算期望是多加上他的概率,最後統計答案時把答案矩陣上所有的期望加和即可,這個方法很完美的被卡精了.......

技術分享
#include <cstdio>
#include <cstring>
#include <vector>
typedef 
long double LD; const int N=200; char s[20]; int alpha,n,m; struct Trie{ int ch[26],fail,deep; bool god; }node[N]; int sz,q[N],size; LD a[N][N],temp_a[N][N],b[N],temp_b[N],need; inline void insert(char *w){ int p=0; for(int i=0;w[i];i++){ if(node[p].ch[w[i]-a]==0)node[p].ch[w[i]-
a]=++sz,node[sz].deep=node[p].deep+1; p=node[p].ch[w[i]-a]; } node[p].god=1; } inline void build(){ q[0]=0; for(int i=0,j=0;i<=j;i++){ for(int l=0;l<alpha;l++) if(node[q[i]].ch[l]) q[++j]=node[q[i]].ch[l], node[q[j]].fail=q[i]?node[node[q[i]].fail].ch[l]:0
, node[q[j]].god=node[q[j]].god||node[node[q[j]].fail].god; else node[q[i]].ch[l]=q[i]?node[node[q[i]].fail].ch[l]:0; } } void dfs(int x){ for(int i=0;i<alpha;i++){ if(node[node[x].ch[i]].god){ a[0][x<<1]+=need; a[1][(x<<1)+1]+=need; a[0][(x<<1)+1]+=need; continue; } a[node[x].ch[i]<<1][x<<1]+=need; a[(node[x].ch[i]<<1)+1][(x<<1)+1]+=need; if(node[node[x].ch[i]].deep>node[x].deep) dfs(node[x].ch[i]); } } inline void Multi_a(){ for(int i=0;i<=size;i++) for(int j=0;j<=size;j++) temp_a[i][j]=0; for(int i=0;i<=size;i++) for(int j=0;j<=size;j++) for(int k=0;k<=size;k++) temp_a[i][j]+=a[i][k]*a[k][j]; for(int i=0;i<=size;i++) for(int j=0;j<=size;j++) a[i][j]=temp_a[i][j]; } inline void Multi_b(){ for(int i=0;i<=size;i++)temp_b[i]=0; for(int i=0;i<=size;i++) for(int j=0;j<=size;j++) temp_b[i]+=a[i][j]*b[j]; for(int i=0;i<=size;i++)b[i]=temp_b[i]; } int main(){ scanf("%d%d%d",&n,&m,&alpha),need=1./alpha; for(int i=1;i<=n;i++) scanf("%s",s),insert(s); build(),dfs(0),size=(sz<<1)+1,b[1]=1.; while(m){ if(m&1)Multi_b(); m>>=1,Multi_a(); } LD ans=0.; for(int i=0;i<=sz;i++)ans+=b[i<<1]; printf("%lf",(double)ans); return 0; }
只是錯了最後一個點

然後我去接受正解,按照我們的思路我們會建出來一個概率矩陣a[i][j]表示由j轉移到i的概率,那就是加上1/alphabet,當然這裏也有從禁忌串直接調回0的操作,然後我們想記錄答案,我們就把矩陣擴大一階,標號為sz+1,就是記錄答案,那麽我們由禁忌串向sz+1加上1/alphabet,那麽我們發現這個矩陣自乘多少次,他的a[sz+1][0]的答案就是我們想要的答案。

經驗教訓:對於矩陣乘法,答案矩陣也可以是方陣,而且在一個矩陣裏意義可以不同。

技術分享
#include <cstdio>
#include <cstring>
#include <vector>
typedef long double LD;
const int N=100;
char s[20];
int alpha,n,m;
struct Trie{
  int ch[26],fail,deep;
  bool god;
}node[N];
int sz,q[N],size;
LD a[N][N],temp[N][N],b[N][N],need;
inline void insert(char *w){
  int p=0;
  for(int i=0;w[i];i++){
    if(node[p].ch[w[i]-a]==0)node[p].ch[w[i]-a]=++sz,node[sz].deep=node[p].deep+1;
    p=node[p].ch[w[i]-a];
  }
  node[p].god=1;
}
inline void build(){
  q[0]=0;
  for(int i=0,j=0;i<=j;i++){
    for(int l=0;l<alpha;l++)
      if(node[q[i]].ch[l])
        q[++j]=node[q[i]].ch[l],
        node[q[j]].fail=q[i]?node[node[q[i]].fail].ch[l]:0,
        node[q[j]].god=node[q[j]].god||node[node[q[j]].fail].god;
      else
        node[q[i]].ch[l]=q[i]?node[node[q[i]].fail].ch[l]:0;
  }
}
void dfs(int x){
    for(int i=0;i<alpha;i++){
        if(node[node[x].ch[i]].god){
            a[0][x]+=need;
            a[sz+1][x]+=need;
            continue;
        }
        a[node[x].ch[i]][x]+=need;
        if(node[node[x].ch[i]].deep>node[x].deep)
            dfs(node[x].ch[i]);
    }
}
inline void Multi_a(){
  for(int i=0;i<=size;i++)
    for(int j=0;j<=size;j++)
      temp[i][j]=0;
  for(int i=0;i<=size;i++)
    for(int j=0;j<=size;j++)
      for(int k=0;k<=size;k++)
        temp[i][j]+=a[i][k]*a[k][j];
  for(int i=0;i<=size;i++)
    for(int j=0;j<=size;j++)
      a[i][j]=temp[i][j];
}
inline void Multi_b(){
  for(int i=0;i<=size;i++)
    for(int j=0;j<=size;j++)
      temp[i][j]=0;
  for(int i=0;i<=size;i++)
    for(int j=0;j<=size;j++)
      for(int k=0;k<=size;k++)
        temp[i][j]+=b[i][k]*a[k][j];
  for(int i=0;i<=size;i++)
    for(int j=0;j<=size;j++)
      b[i][j]=temp[i][j];
}
int main(){
  scanf("%d%d%d",&n,&m,&alpha),need=(LD)1./(LD)alpha;
  for(int i=1;i<=n;i++)
    scanf("%s",s),insert(s);
  build(),dfs(0),size=sz+1,a[size][size]=1.;
  for(int i=0;i<=size;i++)b[i][i]=1.;
  while(m){
    if(m&1)Multi_b();
    m>>=1,Multi_a();
  }
  printf("%.6lf",(double)b[size][0]);
  return 0;
}
AC code

【BZOJ 2553】[BeiJing2011]禁忌 AC自動機+期望概率dp