CF506E Mr. Kitayuta's Gift
这道题神仙到让我面临着买不到冰皮月亮蛋糕的风险来写题解
(蛋糕真好吃呜呜呜)
这篇题解参考了CQzhangyu神仙的做法。
(目测比标程科学好写)
限制是要回文,根据我们做字符串计数的常识,一定是尽量能匹配的先匹配上,这样就不会重复计数了。
因此,可以想到一种直观的DP方法:
令$f[i][j][k]$表示前面匹配到i后面匹配到j加入了k个字符的方案数,我们每次看它是否和前后匹配来转移。
那么它应该是一个有限状态自动机上的转移 大概长这个样子

相当于有向图上路径计数,于是就可以矩乘优化。考虑它有S^3个节点,肯定没戏的。
进一步优化
我们把前后不同的转移标记为红点 它有方案数为24的自环,相同类似的标为绿点。
那么我们发现我们其实并不关心红绿的顺序,我们只关心它出现的次数,最后乘上方案数就可以了。
先考虑我们如何得到这个方案数,用$f[i][j][k]$表示前i个和后j个,走过k个红点的方案数。
有两种转移
1.ch[i]==ch[j]
$f[i+1][j-1][k] += f[i][j][k]$
2.ch[i]!=ch[j]
$f[i+1][j][k]+=f[i][j][k]$
$f[i][j-1][k]+=f[i][j][k]$
我们现在再来看看原来的自动机变成了什么样子

应该是这个样子,每一条链有x个红点 $\lceil \frac{n-x}{2} \rceil$个绿点
如果我们再把它们合并到一张图中,应该长这个样子了

(盗CQzhangyu神仙的图)
我们把方案数直接放到红点与绿点之间的边上。
那么我们就有了一个很科学的解法了,我们现在再在这个图上跑矩乘就ok了。
等等,奇数好像不大对?
我们发现奇数的时候匹配ch[i]和ch[i+1]以后不能直接随便填的转移,它们分别位于中间的两边并且中间的元素填的跟它们一样的时候我们算重了1次,所以方案数多了。
那么我们重新再做一遍类似的矩乘就可以了,现在红点绿点之间的边是强制$ch[i]==ch[i+1]$的方案数。
最后终点还要去掉自环,这样我们的方案数就算对了。
接着你发现你T了!
可以发现我们是个DAG所以按照拓扑序转移可以顺序枚举点,这样加上一个1/6常数就能跑过了qwq
我觉得此题过于神仙QAQ
当然还有更神仙的解法来自cz_xuyixuan巨佬
(讲真我看到这个做法直接惊呆了= =)
还有官方题解的做法,其实就是不缩点而已,个人认为CQzhangyu神仙的解法更为简便。
附代码。
//Love and Freedom.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define inf 20021225
#define mdn 10007
#define N 310
using namespace std;
int read()
{
int s=,f=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-') f=-; ch=getchar();}
while(ch>=''&&ch<='') s=s*+ch-'',ch=getchar();
return f*s;
}
struct mat
{
int a[N][N],n;
void init(){memset(a,,sizeof(a));}
void det(){init(); for(int i=;i<=n;i++) a[i][i]=;}
}s,t;
mat operator*(mat a,mat b) // 1/6!
{
mat tmp; tmp.n=a.n; tmp.init();
for(int i=;i<=a.n;i++) for(int j=i;j<=a.n;j++)
for(int k=i;k<=j;k++) tmp.a[i][j]=(tmp.a[i][j]+a.a[i][k]*b.a[k][j])%mdn;
return tmp;
}
mat ksm(mat bs,int mi,mat &ans)
{
while(mi)
{
if(mi&) ans=ans*bs;
bs=bs*bs; mi>>=;
}
return ans;
}
int f[][][],g[];
char ch[N]; int n,d;
void upd(int &x,int y){x+=x+y>=mdn?y-mdn:y;}
void dp()
{
f[][n][]=;
for(int i=;i<=n;i++) for(int j=n;j>=i;j--)
if(ch[i]==ch[j]) for(int k=;k<i+n-j;k++)
if(i+<j) upd(f[i+][j-][k],f[i][j][k]);
else upd(g[k],f[i][j][k]);
else for(int k=;k<i+n-j;k++)
upd(f[i+][j][k+],f[i][j][k]),
upd(f[i][j-][k+],f[i][j][k]);
}
int main()
{
scanf("%s",ch+); n=strlen(ch+); d=read(); dp();
int top=n+(n+)/+; s.n=t.n=top; s.a[][]=;
s.a[][top-(n+)/]=g[]; t.a[top][top]=;
for(int i=;i<=n;i++)
{
t.a[i][i]=,t.a[i][top-(n-i+)/]=g[i];
if(i!=n) t.a[i][i+]=;
}
for(int i=n+;i<top;i++) t.a[i][i+]=,t.a[i][i]=;
if((n+d)&)
{
ksm(t,d+n+>>,s);
int ans=s.a[][top];
s.init(); t.init(); memset(g,,sizeof(g));
for(int i=;i<n;i++) if(ch[i]==ch[i+])
for(int j=;j<=n;j++) upd(g[j],f[i][i+][j]);
s.a[][]=; s.a[][top-(n+)/]=g[];
for(int i=;i<=n;i++)
{
t.a[i][i]=,t.a[i][top-(n-i+)/]=g[i];
if(i!=n) t.a[i][i+]=;
}
for(int i=n+;i<top;i++) t.a[i][i+]=,t.a[i][i]=;
ksm(t,d+n+>>,s); printf("%d\n",(ans-s.a[][top]+mdn)%mdn);
}
else
ksm(t,d+n>>,s),printf("%d\n",s.a[][top]);
return ;
}
CF506E Mr. Kitayuta's Gift的更多相关文章
- 【CF506E】Mr. Kitayuta's Gift dp转有限状态自动机+矩阵乘法
[CF506E]Mr. Kitayuta's Gift 题意:给你一个字符串s,你需要在s中插入n个字符(小写字母),每个字符可以被插在任意位置.问可以得到多少种本质不同的字符串,使得这个串是回文的. ...
- 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 505A. Mr. Kitayuta's Gift 解题报告
题目链接:http://codeforces.com/problemset/problem/505/A 题目意思:给出一个长度不大于10的小写英文字符串 s,问是否能通过在字符串的某个位置插入一个字母 ...
- Codeforces 505 A Mr. Kitayuta's Gift【暴力】
题意:给出一个字符串,可以向该字符串的任意位置插入一个字母使其变成回文串 因为字符串的长度小于10,枚举插入的字母,和要插入的位置,再判断是否已经满足回文串 #include<iostream& ...
- Codeforces 506E Mr. Kitayuta's Gift (矩阵乘法,动态规划)
描述: 给出一个单词,在单词中插入若干字符使其为回文串,求回文串的个数(|s|<=200,n<=10^9) 这道题超神奇,不可多得的一道好题 首先可以搞出一个dp[l][r][i]表示回文 ...
- 【Codeforces 506E】Mr.Kitayuta’s Gift&&【BZOJ 4214】黄昏下的礼物 dp转有限状态自动机+矩阵乘法优化
神题……胡乱讲述一下思维过程……首先,读懂题.然后,转化问题为构造一个长度为|T|+n的字符串,使其内含有T这个子序列.之后,想到一个简单的dp.由于是回文串,我们就增量构造半个回文串,设f(i,j, ...
- Codeforces Round #286 (Div. 2)A. Mr. Kitayuta's Gift(暴力,string的应用)
由于字符串的长度很短,所以就暴力枚举每一个空每一个字母,出现行的就输出.这么简单的思路我居然没想到,临场想了很多,以为有什么技巧,越想越迷...是思维方式有问题,遇到问题先分析最简单粗暴的办法,然后一 ...
随机推荐
- tomcat在45秒内没有启动,启动超时
在部署的时候出现Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds. If the server ...
- String对象的常见方法
String 对象方法 方法 描述 anchor() 创建 HTML 锚. big() 用大号字体显示字符串. blink() 显示闪动字符串. bold() 使用粗体显示字符串. charAt() ...
- Python学习之==>操作Redis
一.redis简介 1.非关系型数据库 2.数据全部是存在内存里面 3.性能非常好,每秒支持30w次读写 4.可以通过备份数据库,把数据存到磁盘上来实现数据的持久化 二.操作redis 1.strin ...
- 应用安全 - 平台 | 工具 - Nexus漏洞汇总
CVE-2019-5475 Date: 类型: RCE 影响范围: Nexus Repository Manager OSS <= Nexus Repository Manager Pro &l ...
- centos7 VM虚拟机在主机关机重启后,无法ping通
解决办法 1.虚拟机的某些网络相关的服务没有启动,打开电脑的服务启动相关服务 2.打开虚拟机的虚拟网络设置,恢复默认设置即可 3.判定虚拟网卡的网关和centos的网关是否一致,如果不一致,改成一致, ...
- Canvas入门02-绘制直线
主要使用的API有: context.moveTo(x,y) 声明线的起始坐标 context.lineTo(x,y) 声明线的下一个坐标 context.fillStyle 声明线的填充颜色 co ...
- MySQL数据库的连接池问题
3. sqlalchemy设置连接池数量上限设置 SQLALCHEMY_POOL_SIZE = 100 SQLALCHEMY_MAX_OVERFLOW = 0 # 超出连接池数量的连接后,最多可以连接 ...
- Python解释器判断整数相加溢出
溢出,则和的最高位(即符号位)与两个加数都不相同,例如 1)非负数+非负数=负数 2)负数+负数=非负数 那么,假设x为a与b的和,((a^b)>=0 && (x^a)<0 ...
- 图论 test solution
图论 test solution T1:潜伏 题目背景 小悠回家之后,跟着母亲看了很多抗日神剧,其中不乏一些谍战片. 题目描述 解放前夕,北平城内潜伏着若干名地下党员,他们居住在城市的不同位置.现在身 ...
- lb开金矿 QDUOJ 数论
lb开金矿 QDUOJ 数论 原题链接,点我进去 题意 大家都知道lb有n个小弟(编号从2到n+1),他们可以按照规则传递信息:某天编号为i的小弟收到信息后,那么第二天他会给编号为j的小弟传达信息,其 ...