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的增大,改变线段树中节点的信息,然后每次询问我们用树链剖分 ...
随机推荐
- 实验1:Mininet源码安装和可视化拓扑工具
一.实验目的 掌握 Mininet 的源码安装方法和 miniedit 可视化拓扑生成工具. 二.实验任务 使用源码安装 Mininet 的 2.3.0d6 版本,并使用可视化拓扑工具生成一个最简拓扑 ...
- linux 漏洞列表
#CVE #Description #Kernels CVE-2017-1000367 [Sudo](Sudo 1.8.6p7 - 1.8.20) CVE-2017-7494 [Samba Remot ...
- 批处理文件的@echo off
转载:https://blog.csdn.net/zl1zl2zl3/article/details/79218448 @echo off 关闭回显 @echo on 打开回显 ...
- Spring Boot入门系列(二十)快速打造Restful API 接口
spring boot入门系列文章已经写到第二十篇,前面我们讲了spring boot的基础入门的内容,也介绍了spring boot 整合mybatis,整合redis.整合Thymeleaf 模板 ...
- 主厨(第4部分)- ASP. netNET Core和Angular 2 CRUD SPA
下载source - 79.7 KB 介绍 在Master Chef(第1部分)和Master Chef(第2部分)中,我介绍了如何使用ASP.Net Core和Angular JS.在Master ...
- [学习笔记] Tarjan算法求桥和割点
在之前的博客中我们已经介绍了如何用Tarjan算法求有向图中的强连通分量,而今天我们要谈的Tarjan求桥.割点,也是和上篇有博客有类似之处的. 关于桥和割点: 桥:在一个有向图中,如果删去一条边,而 ...
- 自定义Antd Pro 默认元素
概要 通用元素 修改的方式 主页面 标签上的图标 logo 和 系统名称 footer 的配置 loading 页面 最终效果 概要 使用 Antd Pro 来开发前端项目时, 生成的项目模板中, 一 ...
- 超好用的UnixLinux 命令技巧 大神为你详细解读
1.删除一个大文件 我在生产服务器上有一个很大的200GB的日志文件需要删除.我的rm和ls命令已经崩溃,我担心这是由于巨大的磁盘IO造成的,要删除这个大文件,输入: > /path/to/fi ...
- python程序整理(1)
''' 用户登录验证 要求: 1. 系统⾃动⽣成4位随机数. 作为登录验证码. 直接用就好. 这里不用纠结 提示. 生成随机数的办法. from random import randint num = ...
- 【思维】UVA 11300 Spreading the Wealth
题目大意 vjudge链接 有n个人围圆桌而坐,每个人有Ai个金币,每个人可以给左右相邻的人一些金币. 若使得最终所有人金币数相等,求最小金币转移数. 数据范围 n<1000001 样例输入 3 ...