题解 P3715 【[BJOI2017]魔法咒语】

oscar

2017-08-31 11:36:21

Solution

北京省选题的日常就是对数据分类讨论。。。 题目瞪一两眼就能看出是道考AC自动机的题 先看前60%数据,发现没有什么特殊要求,且L比较小 直接考虑dp,对“忌讳词语”建一个AC自动机,然后搞出dp方程: ```cpp if(tmp) dp[i+len[j]][tmp]=(dp[i][id]+dp[i+len[j]][tmp])%(1e9+7); ``` 其中i为dp到第几个字符, tmp为在AC自动机上的第id个点开始,匹配第j个基本词汇后到达的状态,若在匹配过程中遇到“忌讳词语”则不进行转移, len[j]为第j个基本词汇的长度 最终答案为 $ \sum_{id}{dp[L][id]} mod 10^9+7 $ 再考虑第7~8个点,基本词汇长度为1 发现L比较大,可以考虑矩阵快速幂 考虑自动机上每一个节点x,以及从这个节点开始匹配第i个基本词汇达到状态y 这说明从第x个状态长度增加1时可以转移到第y个状态,所以将转移矩阵的第x行第y列+1 这样就解决了 最后两个点基本词汇长度为2 我们只需要把矩阵扩大到原来的两倍,并且按之前的方法在上面标记一下长度为2的基本词汇可以转移到什么位置,然后将矩阵的左下部分设为单位矩阵,就可以解决剩下的数据了 还有一点小细节,矩阵快速幂写得不好可能会导致RE,原因玄学。。 ~~下面的代码是考场代码,可能写得有点不优美,大家凑活着看吧。。~~ 贴代码~~走人~~ ```cpp #include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; struct node { int id,end; node *next[26],*fail; }*root,pool[30001]; int top; string bas[55],ban[55]; int n,m,l,len[111],N;long long ans; int dp[111][111];//[len][id] const long long mod=1000000007ll; struct matrix { int siz; int mat[233][233]; #define int long long matrix(int x=0) { siz=N; for(int i=1;i<=siz;i++) for(int j=1;j<=siz;j++) { mat[i][j]=x*(int)(i==j); } } matrix operator=(const matrix& m) { siz=N; for(int i=1;i<=siz;i++) { for(int j=1;j<=siz;j++) { mat[i][j]=m.mat[i][j]; } } return *this; } matrix operator=(const int x) { siz=N; for(int i=1;i<=siz;i++) for(int j=1;j<=siz;j++) { mat[i][j]=x*(int)(i==j); } return *this; } matrix operator*=(const matrix &m) { matrix tmp; for(int i=1;i<=siz;i++) { for(int j=1;j<=siz;j++) { long long o=0; for(int k=1;k<=siz;k++) { o+=((long long)mat[i][k]*m.mat[k][j])%mod; } o%=mod; tmp.mat[i][j]=o; } } return *this=tmp; } }; inline matrix fexp(matrix &x,int y) { int bit=1<<30; matrix cur=1; while(bit) { cur*=cur; if(y&bit)cur*=x; bit>>=1; } return cur; } #undef int void addword(string str) { int len=str.length(); node *tmp=root; for(int i=0;i<len;i++) { int cur=str[i]-'a'; if(!tmp->next[cur]) { tmp->next[cur]=&pool[++top]; tmp->next[cur]->id=top; } tmp=tmp->next[cur]; } tmp->end=1; } queue<node*> q; void build() { while(!q.empty())q.pop(); q.push(root); while(!q.empty()) { node *cur=q.front();q.pop(); for(int i=0;i<26;i++) { node *nex=cur->next[i],*tmp=cur->fail; if(!nex)continue; while(tmp!=root&&!(tmp->next[i]))tmp=tmp->fail; if(cur==root||!(tmp->next[i])) nex->fail=tmp; else nex->fail=tmp->next[i]; q.push(nex); } } } int match(string str,int len,int start) { node *cur=&pool[start],*tmp; for(int i=0;i<len;i++) { int e=str[i]-'a'; while(cur!=root&&!(cur->next[e]))cur=cur->fail; if(cur->next[e]) cur=cur->next[e]; tmp=cur; while(tmp!=root) { if(tmp->end)return 0; tmp=tmp->fail; } } return cur->id; } int main() { char buf[110]; root=&pool[++top];root->id=1;root->fail=root; //#ifndef DDDDBUG // freopen("sorcery.in","r",stdin); // freopen("sorcery.out","w",stdout); //#endif //DDDDBUG scanf("%d%d%d",&n,&m,&l); for(int i=1;i<=n;i++) { scanf("%s",buf); bas[i]=buf; len[i]=bas[i].length(); } for(int i=1;i<=m;i++) { scanf("%s",buf); ban[i]=buf; addword(ban[i]); } build(); if(l<=100) { dp[0][1]=1; for(int i=0;i<l;i++) { for(int j=1;j<=n;j++) { if(i+len[j]<=l) for(int id=1;id<=top;id++) { int tmp=match(bas[j],len[j],id); if(tmp) dp[i+len[j]][tmp]=(dp[i][id]+dp[i+len[j]][tmp])%mod; } } } for(int id=1;id<=top;id++) { ans=(ans+dp[l][id])%mod; } printf("%lld\n",ans); } else { int notwo=1; N=2*top; matrix mult=0; for(int i=1;i<=top;i++) { mult.mat[i+top][i]=1; } for(int pos=1;pos<=top;pos++) { for(int i=1;i<=n;i++) { if(len[i]==1) { int x=pos,y=match(bas[i],1,pos); if(y) { mult.mat[x][y]++; //mult.mat[x+top][y+top]++;//这里我第一次写挂了,去掉就能AC } } else { int x=pos,y=match(bas[i],2,pos); if(y) mult.mat[x][y+top]++; notwo=0; } } } if(notwo)N>>=1; matrix tmp=fexp(mult,l); matrix orig=0; orig.mat[1][1]=1; orig*=tmp; for(int pos=1;pos<=top;pos++) { ans+=orig.mat[1][pos]; ans%=mod; } printf("%lld\n",ans); } return 0; } ```