1. 程式人生 > >魔法咒語

魔法咒語

精通 struct 開始 bsp amp 學會 nal cout tom

題目描述

Chandra 是一個魔法天才。從一歲時接受火之教會洗禮之後, Chandra 就顯示出對火元素無與倫比的親和力,輕而易舉地學會種種晦澀難解的法術。這也多虧 Chandra 有著常人難以企及的語言天賦,讓她能輕松流利地說出咒語中那些極其拗口的魔法詞匯。直到十四歲,開始學習威力強大的禁咒法術時, Chandra 才遇到了障礙。

根據火之魔法規則,禁咒的構成單位是 NN 個基本詞匯。施法時只要凝聚精神力,說出一段用這些詞語組成的長度恰好等於 LL 的語言,就能釋放威力超乎想象的火法術。過去的魔法師們總結了幾種表達起來最連貫的組合方式,方便施法者以最快語速完成法術。但具有魔法和語言雙重天才的 Chandra 不滿足於這幾種流傳下來的禁咒,因為她可以毫無困難地說出普通人幾乎不可能表達的禁咒語句。然而,在實際施法時, Chandra 發現有些自創禁咒念出後不但沒有預期效果,反而會使自己的精神力迅速枯竭,十分難受。這個問題令 Chandra 萬分不解。她大量閱讀典籍,到處走訪魔法學者,並且不顧精神折磨一次又一次嘗試新咒語,希望找出問題的答案。很多年過去了,在一次遠古遺跡探險中, Chandra 意外闖進了火之神艾利克斯的不知名神殿。根據巖土特征分析,神殿應該有上萬年的歷史,這是極其罕見的。 Chandra 小心翼翼地四處探索,沿著魔力流動來到一間密室。她看見密室中央懸浮著一本書籍。在魔法保護下書籍狀況完好。精通上古語言的 Chandra 讀過此書,終於解開了多年的困惑。禁咒法術之所以威力強大,是因為咒語借用了火之神艾利克斯的神力。這本書裏記載了艾利克斯生平忌諱的 M 個詞語,比如情敵的名字、討厭的植物等等。

使用禁咒法術時,如果語言中含有任何忌諱詞語,就會觸怒神力而失效,施法者也一並遭受懲罰。例如,若 ”banana” 是唯一的忌諱詞語, “an”、 ”ban”、 ”analysis” 是基本詞匯,禁咒長度須是 11, 則“bananalysis” 是無效法術, ”analysisban”、 ”anbanbanban”是兩個有效法術。註意:一個基本詞匯在禁咒法術中可以出現零次、 一次或多次;只要組成方式不同就認為是不同的禁咒法術,即使書寫形式相同。謎題破解, Chandra 心情大好。她決定計算一共有多少種有效的禁咒法術。由於答案可能很大,你只需要輸出答案模 1,000,000,007的結果。


Sol

把禁咒建AC自動機,預處理每一個基本詞匯從每一個點開始跑會跑到哪,會不會碰到禁咒。 然後第一問暴力dp,第二問蒟乘 註意對於一個串,如果他的fail是禁咒,那麽他也是非法點 技術分享圖片
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<string>
#include<queue>
#define
maxn 105 #define mod 1000000007 #define ll long long using namespace std; int n,N,m,l,tr[maxn][26],fail[maxn],p[maxn],tot,cnt,head[maxn]; int f[maxn][105]; string a[maxn]; char ch[maxn]; struct node{ int v,nex,w; }e[1000005]; void ins(){ int len=strlen(ch),k=0; for(int i=0;i<len;i++){ if(!tr[k][ch[i]-a])tr[k][ch[i]-a]=++cnt; k=tr[k][ch[i]-a]; } p[k]=1; } void build(){ queue<int>q; for(int i=0;i<26;i++)if(tr[0][i])q.push(tr[0][i]); while(!q.empty()){ int k=q.front();q.pop(); for(int i=0;i<26;i++){ if(tr[k][i]){ fail[tr[k][i]]=tr[fail[k]][i]; p[tr[k][i]]|=p[tr[fail[k]][i]]; q.push(tr[k][i]); } else tr[k][i]=tr[fail[k]][i]; } } } void add(int t1,int t2,int t3){ e[++tot].v=t2;e[tot].w=t3;e[tot].nex=head[t1];head[t1]=tot; } struct no{ ll v[505][505]; }s,A; no operator *(no a,no b){ no c; for(int i=0;i<=N;i++) for(int j=0;j<=N;j++){ c.v[i][j]=0; for(int k=0;k<=N;k++){ c.v[i][j]=(c.v[i][j]+1LL*a.v[i][k]*b.v[k][j]%mod)%mod; } } return c; } int main() { cin>>n>>m>>l; for(int i=1;i<=n;i++){ scanf("%s",ch); a[i]=(string)ch; } for(int i=1;i<=m;i++){ scanf("%s",ch);ins(); } build(); for(int i=0;i<=cnt;i++){ for(int j=1;j<=n;j++){ int k=i,fl=0;if(p[k])continue; for(int x=0;x<a[j].size();x++){ k=tr[k][a[j][x]-a]; if(p[k]){fl=1;break;} } if(fl)continue; add(i,k,a[j].size()); } } if(l<=100){ f[0][0]=1; for(int x=0;x<=l;x++){ for(int k=0;k<=cnt;k++){ for(int i=head[k];i;i=e[i].nex){ if(e[i].w+x>l)continue; f[e[i].v][x+e[i].w]=(f[e[i].v][x+e[i].w]+f[k][x])%mod; } } } int ans=0; for(int i=0;i<=cnt;i++)ans=(ans+f[i][l])%mod; cout<<ans<<endl; return 0; } N=cnt+cnt+1; for(int k=0;k<=cnt;k++){ s.v[k+cnt+1][k]++; for(int i=head[k];i;i=e[i].nex){ int j=e[i].v; if(e[i].w==1)s.v[k][j]++; else s.v[k][j+cnt+1]++; } } for(int i=0;i<=N;i++)A.v[i][i]=1; while(l){ if(l&1)A=A*s; s=s*s;l>>=1; } ll ans=0; for(int i=0;i<=cnt;i++){ ans=(ans+A.v[0][i])%mod; } cout<<ans<<endl; return 0; }
View Code

魔法咒語