题目梗概
给出一个初始字符串S和目标字符串T,仅包含a,b,c三种字母。
给出三种操作:
1)将字符串S中的一个‘a’字符变成‘b’,并消耗cost0的花费;
2)将字符串S中的一个‘b’字符变成‘c’,并消耗cost1的花费;
3)将字符串S中的一个‘c’字符变成‘a’,并消耗cost2的花费;
在总花费不超过M的限制下,求有多少种方案将S转化为T。
(|S|<=11,M<=1e9)
解题思路
将S转化为T的最小花费我们是可以很快求出的。
接下来就是将每一为的数字做无聊的循环操作,且花费固定的为cost1+cost2+cost3。
于是我们可以求出最大步数=最小步数+(M-最小花费)/(cost1+cost2+cost3)。
之后我们有了一个naive的思路: f[i][j] 表示第i步时,字符串的状态为j,但是这样还是会超时。
可以发现如果要将S转化为T,每个位置操作次数mod 3的值是一定的,并且每个位置相对独立。
于是我们用 f[i][j][k][t] 表示第i步,%3=0的个数,%3=1的个数,%3=2的个数。
不难发现转移关系确定,直接矩乘优化。
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;
const int maxn=15,tt=1000000007;
const int num[3][3]={0,1,3,6,0,2,4,5,0};
const int sum[3][3]={0,1,2,2,0,1,1,2,0};
struct jz{
int n,m;
LL x[105][105];
}A;
char S[maxn],T[maxn];
int c[3],h[3],hash[105][105],tot,MAXc,len;
LL MINc,MAXs,MINs;
jz cheng(jz a,jz b){
jz c;memset(c.x,0,sizeof(c.x));
c.n=a.n;c.m=b.m;
for (int i=1;i<=c.n;i++)
for (int j=1;j<=c.m;j++)
for (int k=1;k<=a.m;k++)
(c.x[i][j]+=(long long)a.x[i][k]*b.x[k][j]%tt)%=tt;
return c;
}
jz qsm(jz w,int b){
jz c;c.n=c.m=tot+1;
for (int i=1;i<=c.n;i++)
for (int j=1;j<=c.m;j++) c.x[i][j]=(i==j);
while(b>0){
if (b%2==1) c=cheng(c,w);
w=cheng(w,w);
b=b>>1;
}
return c;
}
int main(){
freopen("exam.in","r",stdin);
freopen("exam.out","w",stdout);
scanf("%s",S+1);scanf("%s",T+1);
scanf("%d%d%d",&c[0],&c[1],&c[2]);
scanf("%lld",&MAXc);
len=strlen(S+1);
for (int j=1;j<=len;j++){
int x=num[S[j]-'a'][T[j]-'a'];
for (int i=2;i>=0;i--)
if (x>=(1<<i)) MINc+=c[i],x-=(1<<i);
h[sum[S[j]-'a'][T[j]-'a']]++;
MINs+=sum[S[j]-'a'][T[j]-'a'];
}
if (MAXc<MINc) return printf("0\n"),0;
MAXs=MINs+(MAXc-MINc)/(c[0]+c[1]+c[2])*3;
for (int i=0;i<=len;i++)
for (int j=0;i+j<=len;j++)
hash[i][j]=++tot;
for (int i=0;i<=len;i++)
for (int j=0;i+j<=len;j++){
if (i) A.x[hash[i-1][j]][hash[i][j]]=i;
if (j) A.x[hash[i+1][j-1]][hash[i][j]]=j;
A.x[hash[i][j+1]][hash[i][j]]=len-i-j;
}
A.n=tot+1;A.m=tot+1;A.x[tot+1][hash[len][0]]=A.x[tot+1][tot+1]=1;
A=qsm(A,MAXs+1);
printf("%lld\n",A.x[tot+1][hash[h[0]][h[1]]]);
return 0;
}