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 }
View Code

 

 

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 }
View Code

 

 

  

 

 

posted @ 2019-09-07 17:38  SevenDawns  阅读(930)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end