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 }
XJOI NOIP501/511训练22 ttt学字符串的更多相关文章
- Java实现蓝桥杯VIP算法训练 奇变的字符串
试题 算法训练 奇变的字符串 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 将一个字符串的奇数位(首位为第0位)取出,将其顺序弄反,再放回原字符串的原位置上. 如字符串" ...
- [c/c++] programming之路(22)、字符串(三)——字符串封装
项目结构 头文件.h #include<stdio.h> #include<stdlib.h> #include<string.h> //字符串封装,需要库函数 / ...
- Java从0开始学——字符串
#,java中的字符串是不可变的: #,比较两个字符串是不是相等,不能用==,因为那只能确认他们是否指向了同一个字符串对象: #,空串和null是不同的: #,代码点和代码单元 #,代码点表示 ...
- asp.net core 系列 22 EF(连接字符串,连接复原,DbContext)
一.连接字符串 在上二篇中,ASP.NET Core 应用程序连接字符串是写死在ConfigureServices代码中,下面介绍通过配置来实现.连接字符串可以存储在 appsettings.json ...
- XJOI网上同步训练DAY6 T2
思路:记得FJ省队集训好像有过这题,可是我太弱了,根本不懂T_T #include<cstdio> #include<iostream> #include<cmath&g ...
- XJOI网上同步训练DAY6 T1
思路:考试的时候直接想出来了,又有点担心复杂度,不过还是打了,居然是直接A掉,开心啊. 我们发现,Ai<=7,这一定是很重要的条件,我们考虑状态压缩,去枚举路径中出现了哪些数字,然后我们把原来n ...
- XJOI网上同步训练DAY5 T1
思路:考虑得出,最终的集合一定是gcd=1的集合,那么我们枚举n个数中哪个数必须选,然后把它质因数分解,由于质数不会超过9个,可以状态压缩,去得出状态为0的dp值就是答案. #include<c ...
- XJOI网上同步训练DAY5 T3
就是对于一个数,我们去考虑把t*****减到(t-1)9999*的代价. #include<cstdio> #include<cmath> #include<algori ...
- XJOI网上同步训练DAY3 T2
考试的时候已经想出来怎么做了,但是没有时间打了T_T 思路:我们考虑将询问以lim排序,然后树链剖分,把边作为线段树的节点,然后随着询问lim的增大,改变线段树中节点的信息,然后每次询问我们用树链剖分 ...
随机推荐
- 排序算法:归并排序(Merge Sort)
归并排序 归并排序采用了分治策略(divide-and-conquer),就是将原问题分解为一些规模较小的相似子问题,然后递归解决这些子问题,最后合并其结果作为原问题的解. 归并排序将排序数组A[1. ...
- 晶振(crystal)与谐振荡器(oscillator)
参考: 1. https://wenku.baidu.com/view/e609af62f5335a8102d2202f.html 2. 晶体振荡器也分为无源晶振和有源晶振两种类型.无源晶振与有源晶振 ...
- Python 中 pip 工具的安装与使用
pip 是 Python 包管理工具,该工具提供了对Python 包的查找.下载.安装.卸载的功能. 目前如果你在 python.org 下载最新版本的安装包,则是已经自带了该工具. Python 2 ...
- JAVA对象头详解(含32位虚拟机与64位虚拟机)
为什么要学习Java对象头 学习Java对象头主要是为了解synchronized底层原理,synchronized锁升级过程,Java并发编程等. JAVA对象头 由于Java面向对象的思想,在JV ...
- 怎样学好 java ?
浅谈Java的学习之路--怎样学好JAVA ?Java - 近10年来计算机软件发展过程中的传奇,其在众多开发者心中的地位就如"屠龙刀"."倚天剑". Java ...
- 以太坊PoW
ethash ethash(eth+hash)是以太坊设计的挖矿算法,为了实现ASIC-resistance,ethash依赖于对内存资源的访问,是一种memory-hard函数.同时为了支持轻节点对 ...
- GCC编译选项笔记
警告选项 -Wall:开启大多数的警告信息 -Wextra:开启额外的警告信息,比如参数未使用警告(-Wunused-parameter) -Werror:将警告当作错误,中断编译 优化选项 -O,- ...
- 双栈排序(洛谷P1155)二分图的判定+思维贪心
题目:戳这里 题目大意: 给你一个数列,问能否通过两个栈的push与pop把它输出成一个升序序列(每个数只能入队并出队一次) 不能的话输出0,能的话输出操作方法 主要思路: 1.判断是否可以成功输出升 ...
- linux(centos8):用systemctl管理war包形式的jenkins(java 14 / jenkins 2.257)
一,如何安装jenkins? 参见: https://www.cnblogs.com/architectforest/p/13685904.html 说明:刘宏缔的架构森林是一个专注架构的博客,地址: ...
- 第四章 NFS服务相关介绍
一.NFS服务介绍 1.什么是NFS?是一个共享存储,文件服务器 2.NFS基本概述NFS是Network File System的缩写及网络文件系统.NFS主要功能是通过局域网络让不同的主机系统之间 ...