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的应用)
由于字符串的长度很短,所以就暴力枚举每一个空每一个字母,出现行的就输出.这么简单的思路我居然没想到,临场想了很多,以为有什么技巧,越想越迷...是思维方式有问题,遇到问题先分析最简单粗暴的办法,然后一 ...
随机推荐
- 洛谷P1190 接水问题
题目名称:接水问题 题目来源 [洛谷P1190] (https://www.luogu.org/problemnew/show/P1190) 题目描述 学校里有一个水房,水房里一共有\(m\)个龙头 ...
- 五、RF中UI自动化操作基础
列表分类 1.打开浏览器 Open Browser url browser [ url | browser=firefox | alias=None | remote_url=False | ...
- python检测编码
# -*- coding: utf-8 -*- import chardet import urllib #可根据需要,选择不同的数据 TestData = urllib.urlopen('http: ...
- ArcCatalog连接远程ArcGIS Server服务器
注意:本地机器登陆的用户名和密码必须与ArcGIS Server服务器上的用户名和密码完全一致,并加入到agsadmin和agsuser组中.重启电脑. (其实就是在自己的电脑上建立一个用户名,这 ...
- 使用Callable或DeferredResult实现springmvc的异步请求
使用Callable实现springmvc的异步请求 如果一个请求中的某些操作耗时很长,会一直占用线程.这样的请求多了,可能造成线程池被占满,新请求无法执行的情况.这时,可以考虑使用异步请求,即主线程 ...
- 阶段3 1.Mybatis_09.Mybatis的多表操作_3 完成account的一对一操作-通过写account的子类方式查询
先把多表查询的sql语句写出来 想要显示的字段 创建一个AccountUser类 继承Account.这样它就会从父类上继承一些信息 这里只需要定义username和address就可以了 .然后生成 ...
- Linux setup
Install centos 7: Config Network a config example in /etc/sysconfig/network-scripts/ifcfg-ens160 TYP ...
- [BZOJ 4455] [ZJOI 2016] 小星星 (树形dp+容斥原理+状态压缩)
[BZOJ 4455] [ZJOI 2016] 小星星 (树形dp+容斥原理+状态压缩) 题面 给出一棵树和一个图,点数均为n,问有多少种方法把树的节点标号,使得对于树上的任意两个节点u,v,若树上u ...
- 在centos6.4下安装python3.5
1.安装依赖包 ./configure --prefix=/usr/local/python3.5 --enable-shared make && make install yum g ...
- Python基础编程闭包与装饰器
闭包的定义 闭包是嵌套在函数中的函数. 闭包必须是内层函数对外层函数的变量(非全局变量)的引用. 闭包格式: def func(): lst=[] def inner(a): lst.append(a ...