XJOI NOIP501/511训练22 ttt学字符串
ttt学字符串
题目大意
大法师ttt 最近在学习字符串,有一天他看到魔力xtx左手拿着A字符串,右手拿着B字符串,两个字符串的长度都是一样的,而且都由abc三种字符构成,大法师ttt的灵力值为C,现在告诉你 a变b b变c以及c变a分别需要消耗的灵力值(其他变换是不存在的),问你在ttt的灵力值范围内最多能有多少种不同的方案可以从xtx的左手串变换到xtx的右手串
输入格式:
前两行输入两个长度相同的字符串 ,第一个串表示左手串,第二个串表示右手串,右手串不能变
第三行输入三个整数分别代表转换的代价cost_ab cost_bc cost_ca
第四行输入一个整数表示ttt的灵力值
输出格式:
对于每组数据输出一个数,表示答案
答案为有序的合法操作序列数对1e9+7取模,
样例输入:
a
a
1 2 3
12
样例输出:
3
样例解释:
第一种方案:不花代价已经可以达成目的
第二种方案:花费6的代价从a -> b -> c -> a
第三种方案 : 第二种方案重复一遍
数据范围:
n 为字符串长度
- 30%, n <= 5, max(cost_ab, cost_bc, cost_ca) <= 5, C <= 20
- 60%, n <= 5, max(cost_ab, cost_bc, cost_ca) <= 100, C <= 1000
- 100%, n <= 11, max(cost_ab, cost_bc, cost_ca) <= 100, C <= 1000000000
时间限制:
1000 ms
空间限制:
32 MB
DP+矩阵快速幂
60分:
记$dp[i][j][k]$表示用了i次魔法之后的字符串A与字符串B相同的有$i$个,可以通过一次变化得到相同的有$j$个
$n$为字符串的长度
那么可以得到通过两次变化的有$n-i-j$个
那么一次魔法可以有3种选择:改相同的,改还有一次变化的,改还有两次变化的
转移方程为
$dp[i][j][k]=dp[i-1][j-1][k+1]*(k+1)+dp[i-1][j][k-1]*(n-j-k+1)+dp[i-1][j+1][k]*(j+1)$
那么现在要考虑这些状态是否可行
先处理出将字符串A变为字符串B的最小代价是多少
因为在改成相同之后,在进行变化需要3次的操作
那么可以算出最大的操作步数$step$
那么答案为$\sum_{i=0}^{step}dp[i][n][0]$
1 #include <bits/stdc++.h> 2 #define mod 1000000007 3 #define ll long long 4 using namespace std; 5 ll ab,bc,ca,C,dp[1100][7][7]; 6 ll t0,t1,n,step,cost,tot,ans; 7 string a,b; 8 int main() 9 { 10 cin>>a>>b; 11 scanf("%lld%lld%lld%lld",&ab,&bc,&ca,&C); 12 tot=ab+bc+ca; 13 for (ll i=0;i<(ll)a.size();i++) 14 { 15 if (a[i]==b[i]) 16 t0++; 17 else 18 { 19 if (a[i]=='c' && b[i]=='a') 20 t1++; 21 if (a[i]=='b' && b[i]=='c') 22 t1++; 23 if (a[i]=='a' && b[i]=='b') 24 t1++; 25 } 26 } 27 for (ll i=0;i<(ll)a.size();i++) 28 { 29 if (a[i]==b[i]) 30 continue; 31 if (a[i]=='a' && b[i]=='b') 32 cost+=ab,step++; 33 if (a[i]=='a' && b[i]=='c') 34 cost+=ab+bc,step+=2; 35 if (a[i]=='b' && b[i]=='c') 36 cost+=bc,step++; 37 if (a[i]=='b' && b[i]=='a') 38 cost+=bc+ca,step+=2; 39 if (a[i]=='c' && b[i]=='a') 40 cost+=ca,step++; 41 if (a[i]=='c' && b[i]=='b') 42 cost+=ca+ab,step+=2; 43 } 44 if (cost>C) 45 { 46 printf("0\n"); 47 return 0; 48 } 49 step+=3*((C-cost)/tot);//计算最大步数 50 dp[0][t0][t1]=1;//初始状态 51 n=(ll)a.size(); 52 for (ll i=1;i<=step;i++) 53 { 54 for (ll j=0;j<=n;j++) 55 { 56 for (ll k=0;k<=n;k++) 57 { 58 if (j+k>n) 59 break; 60 if (j>0 && k<n) 61 dp[i][j][k]=(dp[i][j][k]+dp[i-1][j-1][k+1]*(k+1)%mod)%mod; 62 if (k>0) 63 dp[i][j][k]=(dp[i][j][k]+dp[i-1][j][k-1]*(n-j-k+1)%mod)%mod; 64 if (j<n) 65 dp[i][j][k]=(dp[i][j][k]+dp[i-1][j+1][k]*(j+1)%mod)%mod; 66 } 67 } 68 } 69 for (ll i=0;i<=step;i++) 70 ans=(ans+dp[i][n][0])%mod;//累加答案 71 printf("%lld\n",ans); 72 }
100分:
可以发现这个转移方程可以用矩阵优化
在矩阵中只要记录前一层的dp值即可
并填入相应的转移矩阵$tr$
但由于答案是$dp[i][n][0]$的前缀和,需要处理出每个$dp[i][n][0]$
记初始矩阵为$f$
那么需要处理的矩阵为
$f+f*tr+f*tr^{2}+...+f*tr^{step}$
这是等比矩阵求和的问题
转移即可
1 #include <bits/stdc++.h> 2 #define mod 1000000007 3 #define ll long long 4 using namespace std; 5 ll ab,bc,ca,C,id[15][15][15]; 6 ll t0,t1,n,step,cost,tot,ans,cnt; 7 string a,b; 8 struct node 9 { 10 ll n,num[300][300]; 11 void init() 12 { 13 for (ll i=1;i<=n;i++) 14 num[i][i]=1; 15 } 16 void prepare() 17 { 18 for (ll i=1;i<=n;i++) 19 { 20 for (ll j=1;j<=n;j++) 21 num[i][j]=0; 22 } 23 } 24 void print() 25 { 26 for (ll i=1;i<=n;i++) 27 { 28 for (ll j=1;j<=n;j++) 29 printf("%lld ",num[i][j]); 30 printf("\n"); 31 } 32 } 33 }tr,f; 34 node A; 35 node operator * (node a,node b) 36 { 37 node c; 38 c.n=a.n; 39 c.prepare(); 40 for (ll i=1;i<=a.n;i++) 41 { 42 for (ll j=1;j<=a.n;j++) 43 { 44 for (ll k=1;k<=a.n;k++) 45 { 46 c.num[i][j]=(c.num[i][j]+a.num[i][k]*b.num[k][j])%mod; 47 } 48 } 49 } 50 return c; 51 } 52 node m_pow(node a,ll b) 53 { 54 node c; 55 c.n=a.n; 56 c.prepare(); 57 c.init(); 58 while (b>0) 59 { 60 if (b&1) 61 c=c*a; 62 b>>=1; 63 a=a*a; 64 } 65 return c; 66 } 67 int main() 68 { 69 cin>>a>>b; 70 scanf("%lld%lld%lld%lld",&ab,&bc,&ca,&C); 71 tot=ab+bc+ca; 72 for (ll i=0;i<(ll)a.size();i++) 73 { 74 if (a[i]==b[i]) 75 t0++; 76 else 77 { 78 if (a[i]=='c' && b[i]=='a') 79 t1++; 80 if (a[i]=='b' && b[i]=='c') 81 t1++; 82 if (a[i]=='a' && b[i]=='b') 83 t1++; 84 } 85 } 86 for (ll i=0;i<(ll)a.size();i++) 87 { 88 if (a[i]==b[i]) 89 continue; 90 if (a[i]=='a' && b[i]=='b') 91 cost+=ab,step++; 92 if (a[i]=='a' && b[i]=='c') 93 cost+=ab+bc,step+=2; 94 if (a[i]=='b' && b[i]=='c') 95 cost+=bc,step++; 96 if (a[i]=='b' && b[i]=='a') 97 cost+=bc+ca,step+=2; 98 if (a[i]=='c' && b[i]=='a') 99 cost+=ca,step++; 100 if (a[i]=='c' && b[i]=='b') 101 cost+=ca+ab,step+=2; 102 } 103 if (cost>C) 104 { 105 printf("0\n"); 106 return 0; 107 } 108 n=(ll)a.size(); 109 step+=3*((C-cost)/tot); 110 for (ll i=0;i<=n;i++) 111 { 112 for (ll j=0;j<=n;j++) 113 { 114 for (ll k=0;k<=n;k++) 115 { 116 if (i+j+k!=n) 117 continue; 118 cnt++; 119 id[i][j][k]=cnt; 120 if (i==t0 && j==t1) 121 f.num[1][cnt]=1;//填入初始矩阵 122 } 123 } 124 } 125 for (ll i=0;i<=n;i++)//根据DP转移方程填入转移矩阵 126 { 127 for (ll j=0;j<=n;j++) 128 { 129 for (ll k=0;k<=n;k++) 130 { 131 if (i+j+k!=n) 132 continue; 133 if (i>0 && j<n) 134 tr.num[id[i-1][j+1][k]][id[i][j][k]]=j+1; 135 if (j>0 && k<n) 136 tr.num[id[i][j-1][k+1]][id[i][j][k]]=k+1; 137 if (i<n && k>0) 138 tr.num[id[i+1][j][k-1]][id[i][j][k]]=i+1; 139 } 140 } 141 } 142 f.n=tr.n=cnt; 143 A.n=cnt*2;//计算等比矩阵求和 144 A.prepare(); 145 for (ll i=1;i<=tr.n;i++) 146 { 147 for (ll j=1;j<=tr.n;j++) 148 A.num[i][j]=A.num[i][j+tr.n]=tr.num[i][j]; 149 } 150 for (ll i=tr.n+1;i<=2*tr.n;i++) 151 { 152 for (ll j=tr.n+1;j<=2*tr.n;j++) 153 { 154 if (i==j) 155 A.num[i][j]=1; 156 } 157 } 158 A=m_pow(A,step); 159 for (ll i=1;i<=tr.n;i++) 160 { 161 for (ll j=tr.n+1;j<=2*tr.n;j++) 162 tr.num[i][j-tr.n]=A.num[i][j]; 163 } 164 f=f*tr; 165 if (t0==n && t1==0) 166 f.num[1][id[n][0][0]]++; 167 printf("%lld\n",f.num[1][id[n][0][0]]); 168 }