【CF506E】Mr. Kitayuta's Gift dp转有限状态自动机+矩阵乘法
【CF506E】Mr. Kitayuta's Gift
题意:给你一个字符串s,你需要在s中插入n个字符(小写字母),每个字符可以被插在任意位置。问可以得到多少种本质不同的字符串,使得这个串是回文的。答案对10007取模。
$|s|\le 200,n\le 10^9$
题解:神题。
首先由于题目要求本质不同,所以我们为了防止重复,考虑从两边向中间不断复原回文串,如果新加入的字符与s两端(或一端)的字符相同,则匹配成功,继续匹配下一个字符。也就是说我们取的是s在回文串中最外面的出现位置。
为了方便,我们先只考虑n+|s|为偶数的情况,可以设出DP状态:设f[i][a][b]表示从外往里加入了i个字符,原串左边匹配到了a,有边匹配到了b的方案数。当新加入一个字符时,我们根据它是否与a和b匹配来确定转移到哪个状态。我们发现这个过程其实是在一个有限状态自动机上匹配的过程。对于s=abaac,转移的过程如下图:

其中GOAL代表匹配完毕,它之后可以接任何字符,所以有26条自环。对于红点,它代表的状态两端的字符不相同,所以他有2条出边,24条自环。对于绿点,它代表的状态两端的字符相同,所以有1条出边和25条自环。特别地,能转移到GOAL的点都是绿点。
直接建出来这个自动机显然节点数目是$|s|^2$的,无法用矩乘优化。所以我们考虑对这个自动机进行压缩。可以发现,整个自动机其实可以被拆成若干条链,其中一条链上如果有i个红点,就有$\lceil {|s|-i\over 2}\rceil$个绿点。既然我们已经把自动机拆成链了,那么每条链上红点绿点的顺序也就无关紧要了,我们只需要知道每条链上红点与绿点的数目。换句话说,我们需要知道有多少条链有i个红点,这样一来本质不同的链就只有|s|条了。
统计方法比较简单,用g[a][b][i]表示在所有从起始节点走到(i,j)这个节点的路径中,有多少条已经走了i个红点。转移复杂度$|s|^3$。
既然我们已经有了|s|种链的各自数目,我们就可以想办法用$O(|s|)$个节点来构建一个新的自动机了。到这里我的方法和官方做法出现了分歧,个人认为我的做法比较简单。
构建|s|个红点串成一串,从起点指出来;$\lceil{|s|\over 2}\rceil$个绿点串成一串,指向终点。对于一种串$(i,\lceil {|s|-i\over 2}\rceil)$,我们从第i个红点向第$\lceil {|s|-i\over 2}\rceil$个绿点连一条边即可。这样一来点数就是${3\over 2}|s|$,可以用矩乘优化。如下图:

那么对于n+|s|为奇数的情况呢?我们先进行$n+|s|+1\over 2$步矩乘,但是在最后一步时形如(i,i+1)的绿点是不能直接转移到终点的。于是我们要将这些转移的贡献减去,方法是将(i,i+1)这样的点设为终点(无自环),重新建图跑一边矩乘即可。
本题的矩乘优化常数小技巧:发现我们只能从编号小的点转移到编号大的点,所以j只需要从i枚举到n,k只需要从i枚举到j即可。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm> using namespace std; const int P=10007;
int n,m,N;
int f[210][210][210],g[210];
struct M
{
int v[310][310];
int * operator [] (const int &a) {return v[a];}
M () {memset(v,0,sizeof(v));}
inline M operator * (const M &a) const
{
M b;
int i,j,k;
for(i=0;i<=N;i++) for(j=i;j<=N;j++) for(k=i;k<=j;k++) b.v[i][j]=(b.v[i][j]+v[i][k]*a.v[k][j])%P;
return b;
} }S,T;
char str[210];
inline void pm(int y)
{
while(y)
{
if(y&1) S=S*T;
T=T*T,y>>=1;
}
}
int main()
{
scanf("%s%d",str,&m),n=strlen(str);
int i,j,k;
f[0][n-1][0]=1;
for(i=0;i<n;i++)
{
for(j=n-1;j>=i;j--)
{
if(str[i]==str[j])
{
for(k=0;k<i+n-j;k++)
{
if(i+1<j) f[i+1][j-1][k]=(f[i+1][j-1][k]+f[i][j][k])%P;
else g[k]=(g[k]+f[i][j][k])%P;
}
}
else
{
for(k=0;k<i+n-j;k++)
{
f[i+1][j][k+1]=(f[i+1][j][k+1]+f[i][j][k])%P;
f[i][j-1][k+1]=(f[i][j-1][k+1]+f[i][j][k])%P;
}
}
}
}
N=n+(n+1)/2+1;
S[0][1]=1,S[0][N-(n+1)/2]=g[0],T[N][N]=26;
for(i=1;i<=n;i++)
{
T[i][i]=24,T[i][N-(n-i+1)/2]=g[i];
if(i!=n) T[i][i+1]=1;
}
for(i=n+1;i<N;i++) T[i][i+1]=1,T[i][i]=25;
if((n+m)&1)
{
pm((n+m+1)>>1);
int ans=S[0][N];
memset(S.v,0,sizeof(S.v));
memset(T.v,0,sizeof(T.v));
memset(g,0,sizeof(g));
for(i=0;i<n-1;i++) if(str[i]==str[i+1]) for(k=0;k<=n;k++)
{
g[k]=(g[k]+f[i][i+1][k])%P;
}
S[0][1]=1,S[0][N-(n+1)/2]=g[0];
for(i=1;i<=n;i++)
{
T[i][i]=24,T[i][N-(n-i+1)/2]=g[i];
if(i!=n) T[i][i+1]=1;
}
for(i=n+1;i<N;i++) T[i][i+1]=1,T[i][i]=25;
pm((n+m+1)>>1);
printf("%d",(ans-S[0][N]+P)%P);
}
else
{
pm((n+m)>>1);
printf("%d",S[0][N]);
}
return 0;
}//abaac 2
【CF506E】Mr. Kitayuta's Gift dp转有限状态自动机+矩阵乘法的更多相关文章
- 【Codeforces 506E】Mr.Kitayuta’s Gift&&【BZOJ 4214】黄昏下的礼物 dp转有限状态自动机+矩阵乘法优化
神题……胡乱讲述一下思维过程……首先,读懂题.然后,转化问题为构造一个长度为|T|+n的字符串,使其内含有T这个子序列.之后,想到一个简单的dp.由于是回文串,我们就增量构造半个回文串,设f(i,j, ...
- CF506E Mr. Kitayuta's Gift
这道题神仙到让我面临着买不到冰皮月亮蛋糕的风险来写题解 (蛋糕真好吃呜呜呜) 这篇题解参考了CQzhangyu神仙的做法. (目测比标程科学好写) 限制是要回文,根据我们做字符串计数的常识,一定是尽量 ...
- Codeforces 505A Mr. Kitayuta's Gift 暴力
A. Mr. Kitayuta's Gift time limit per test 1 second memory limit per test 256 megabytes input standa ...
- 水题 Codeforces Round #286 (Div. 2) A Mr. Kitayuta's Gift
题目传送门 /* 水题:vector容器实现插入操作,暴力进行判断是否为回文串 */ #include <cstdio> #include <iostream> #includ ...
- codeforces Round 286# problem A. Mr. Kitayuta's Gift
Mr. Kitayuta has kindly given you a string s consisting of lowercase English letters. You are asked ...
- Codeforces 506E Mr. Kitayuta's Gift (矩阵乘法,动态规划)
描述: 给出一个单词,在单词中插入若干字符使其为回文串,求回文串的个数(|s|<=200,n<=10^9) 这道题超神奇,不可多得的一道好题 首先可以搞出一个dp[l][r][i]表示回文 ...
- Codeforces 506E - Mr. Kitayuta's Gift(神仙矩阵乘法)
Codeforces 题目传送门 & 洛谷题目传送门 神仙题 %%%%%%%%%%%%% u1s1 感觉这道题风格很省选( 下记 \(m=|s|\),首先探讨 \(n+m\) 为偶数的情形. ...
- [BZOJ 1009] [HNOI2008] GT考试 【AC自动机 + 矩阵乘法优化DP】
题目链接:BZOJ - 1009 题目分析 题目要求求出不包含给定字符串的长度为 n 的字符串的数量. 既然这样,应该就是 KMP + DP ,用 f[i][j] 表示长度为 i ,匹配到模式串第 j ...
- BZOJ 1009 GT考试 (AC自动机 + 矩阵乘法加速dp)
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1009 题意: 准考证号为\(n\)位数\(X_1X_2....X_n(0<=X_ ...
随机推荐
- Android Studio下加入百度地图的使用(二)——定位服务
上一章(http://www.cnblogs.com/jerehedu/p/4891216.html)中我们已经完成了环境的搭建,这一章我们来研究一下如何使用. 第一步:在xml文件中加入以下权限 & ...
- AndroidStudio下加入百度地图的使用(四)——路线规划
上一章中我们已经完成了API常用的方法及常量属性的使用,这一章向大家介绍一下地图的重要功能-路线规划. (1) 定义API中的路线查询类: RoutePlanSearch mSearch = Rout ...
- 【Spark】Spark性能调优
官网:http://spark.apache.org/docs/latest/tuning.html 1.引言 提到Spark与Hadoop的区别,基本最常说的就是Spark采用基于内存的计算方式,尽 ...
- 一文看懂 Dubbo 的集成与使用
前言 今年年初时,阿里巴巴开源的高性能服务框架dubbo又开始了新一轮的更新,还加入了Apache孵化器.原先项目使用了spring cloud之后,已经比较少用dubbo.目前又抽调回原来的行业应用 ...
- MySQL数据切分的相关概念和原理详解
对于数据切分,我们可能还不是很熟悉,但是它对于MySQL数据库来说也是相当重要的一门技术,本文我们就详细介绍一下MySQL数据库的数据切分的相关知识,接下来就让我们一起来了解一下这部分内容. 什么是数 ...
- More than the maximum number of request parameters
前些时间,我们的的一个管理系统出现了点问题,原本运行的好好的功能,业务方突然讲不行了,那个应用已经运行了好多年了,并且对应的代码最近谁也没改动过,好奇怪的问题,为了解决此问题,我们查看了日志,发现请求 ...
- IDEA环境设置
设置SDK:https://blog.csdn.net/y999666/article/details/51893348 打开模板使用说明,找到Maven本地安装目录, 备份E:\Program Fi ...
- s:iterator 标签使用错误记录
<s:iterator value="newMarriageMoveList" id='tpNewMarriage' status="number"> ...
- PureFTP被动端口设置
修改Pureftp的配置文件把 # PassivePortRange 30000 50000 把前面的#删除 重启pureftpd 注意把被动端口防火墙例外 如果是阿里云主机 安全规 ...
- GDB用法简要整理
[时间:2017-05] [状态:Open] [关键词:gdb,调试,debug,用户手册] 使用gdb是需要在编译是指定-g命令,在可执行文件中添加符号信息. 1. 启动和退出 可以使用gdb gd ...