Cowlphabet 奶牛文字
- Description
约翰家的奶牛用别人听不懂的“牛语”联络。牛语采用英文字母,而且区分大小写。牛语中的语法中,前后字母的衔接非常重要,存在P个基本组合,每个字母之后只能接固定的几个字母。
约翰担心奶牛正在密谋反对他,于是最近一直在偷听她们的对话。可是牛语太复杂了,他只模模糊糊地听到了一个单词,并确定了这个单词中有U个大写字母,L个小写字母。约翰对这个单词很在意,他想知道,有多少牛语词汇拥有U个大写字母,L个小写字母。由于这个数字太大了,你只要输出答案取97654321的余数就可以了。
- Input Format
第一行:三个用空格隔开的整数:U,L和P,1≤U,L≤250,1≤P≤200;
第二行到P+1行:第i+1有两个字母Ai和Bi,表示字母Ai后面可以接Bi,没有一对Ai和Bi是完全相同的。
- Output Format
第一行:单个整数,表示符合条件的词汇数量除以97654321的余数。
- Sample Input
2 2 7
AB
ab
BA
ba
Aa
Bb
bB
- Sample Output
7
- Hint
可能的单词为 AabB,ABba,abBA,BAab,BbBb,bBAa,bBbB
- 分析
一开始还以为是道图论题,建图跑了半天。后来才发现要求方案数,那就用动规好了。
记
F[i][j][k]
表示已知i个字母,其中有j个大写字母,最后一个字母是k。我写的转移方程是正推的:
F[i+1][j+(E[l].to<=′Z′)][E[l].to]+=F[i][j][k]
处理一下初始值
F[1][1][′A′…′Z′]=1,F[1][0][′a′…′z′]=1
,就能过了。
#include <queue>
#include <stack>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int Mod=97654321;
long long F[502][252][70],U,L,P,Ans,tot,last[70];
char s[2];
struct Data{int to,next;}E[252];
void Addline(int u,int v){E[++tot].to=v;E[tot].next=last[u];last[u]=tot;}
int main(){
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
scanf("%lld%lld%lld\n",&U,&L,&P);
for (int i=1;i<=P;i++){
gets(s);
Addline(s[0]-'A',s[1]-'A');
}
for (int i='A'-'A';i<='Z'-'A';i++) F[1][1][i]=1;
for (int i='a'-'A';i<='z'-'A';i++) F[1][0][i]=1;
for (int i=1;i<U+L;i++){
for (int j=0;j<=U;j++){
for (int k='A'-'A';k<='Z'-'A';k++){
for (int l=last[k];l;l=E[l].next){
(F[i+1][j+(E[l].to<=('Z'-'A'))][E[l].to]+=F[i][j][k])%=Mod;
}
}
for (int k='a'-'A';k<='z'-'A';k++){
for (int l=last[k];l;l=E[l].next){
(F[i+1][j+(E[l].to<=('Z'-'A'))][E[l].to]+=F[i][j][k])%=Mod;
}
}
}
}
for (int i='A'-'A';i<='Z'-'A';i++) (Ans+=F[U+L][U][i])%=Mod;
for (int i='a'-'A';i<='z'-'A';i++) (Ans+=F[U+L][U][i])%=Mod;
printf("%lld",Ans);
fclose(stdin); fclose(stdout);
return 0;
}