[矩阵快速幂 优化DP] 51Nod 1311 转换机

题目梗概

给出一个初始字符串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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值