【Codeforces 506E】Mr.Kitayuta’s Gift&&【BZOJ 4214】黄昏下的礼物 dp转有限状态自动机+矩阵乘法优化

神题……
胡乱讲述一下思维过程……
首先,读懂题.
然后,转化问题为构造一个长度为|T|+n的字符串,使其内含有T这个子序列.
之后,想到一个简单的dp.由于是回文串,我们就增量构造半个回文串,设f(i,j,k)为构造到第i个位置,从前往后匹配到j,从后往前匹配到k,这样O(m*m*n)(没有观察到其转移的性质会再乘个26).
再然后,发现不妙,在最后讨论奇偶.
(我的思路到此为止……)
接着,观察其转移的实质,发现其实dp的过程就是在一个有限状态自动机上行走,而有限状态自动机上的状态就是目前剩下的T,所以我们只不过是要求出从起点到某些点的路径条数(事先声明步数).
到了现在,我们的目的只不过是统计到不同终点的路径条数,那么:第一步,点集相同的路径合并起来,点集不同,一定不同;第二步,对于点集相同的路径们,他们走自环的总数一定,分配不同,一定不同;第三步,对于点集相同且分配相同的路径们,他们每次走自环,走得不同,一定不同.接着观察,对于自环数一定的状态,是一样的,而且只有3种,并且他们的顺序也没有关系了(可以从组合数的角度看出),至此,对于每一种状态数量都相同且点集相同的路径,我们都可以合并,并且还可以算出合并之后的路径类在不计算内部影响下的贡献和.
好了,现在该怎么处理上面最后合并出来的每一种路径的内部贡献呢?直接在原自动机上搞,肯定不行,每一种处理一次好一些,但是还是过不了,我们考虑建立一个大图,利用邻接矩阵的次幂的性质,我们就可以直接get到每两个点之间的距离,我们只要保证在这个大图上能get到所有我们想要的信息就可以了,所以现在我们把之前的自动机合并成一个大自动机就ok了.
最后,把前两步的信息合并,就好了.
好牛逼的有限状态自动机啊,感觉就像一个两端匹配的AC自动机,而且匹配的还是子序列.
还有这个组合数学也是牛的一笔,甚至还利用组合意义合并路径.
而且那个转大图建立邻接矩阵也是超66666666666666666
(似乎还有组合数做法……看不懂……)
(把dp状态搞到有限状态自动机上似乎是套路???)

#pragma GCC optimize("O3")
#include <cstdio>
#include <cstring>
#include <algorithm>
const int P=10007,N=201;
char s[N];
int n,m,f[N][N][2],dp[N][N][N],R,G,B,S,a[N<<1][N<<1],temp[N<<1][N<<1],b[N<<1][N<<1];
inline void DP(){
  register int i,j,l,r;
  dp[1][m][0]=1;
  for(i=m;i>0;--i)
    for(l=1,r=i;r<=m;++l,++r)
      for(j=0;j<=(m-i);++j){
        if(!dp[l][r][j])continue;
        if(s[l]==s[r]){
          if(l+1==r)
            (dp[0][0][j]+=dp[l][r][j])%=P;
          else if(l==r){
            (dp[0][0][j]+=dp[l][r][j])%=P;
            if(n&1)(f[j][(m-j+1)>>1][0]+=dp[l][r][j])%=P;
          }else
            (dp[l+1][r-1][j]+=dp[l][r][j])%=P;
        }else{
          (dp[l+1][r][j+1]+=dp[l][r][j])%=P;
          (dp[l][r-1][j+1]+=dp[l][r][j])%=P;
        }
      }
  for(i=0;i<=m;++i)
    (f[i][(m-i+1)>>1][1]+=((n&1)?26:1)*dp[0][0][i])%=P;
}
#define red(a) (a)
#define green(a) (R+(a))
#define blue(a) (R+G+(a))
inline void Multi1(){
  memset(temp,0,sizeof(temp));
  register int i,j,k;
  for(i=1;i<=S;++i)
    for(j=1;j<=S;++j)
      if(a[i][j])
        for(k=1;k<=S;++k)
          if(b[j][k])
            (temp[i][k]+=a[i][j]*b[j][k])%=P;
  memcpy(b,temp,sizeof(b));
}
inline void Multi2(){
  memset(temp,0,sizeof(temp));
  register int i,j,k;
  for(i=1;i<=S;++i)
    for(j=1;j<=S;++j)
      if(a[i][j])
        for(k=1;k<=S;++k)
          if(a[j][k])
            (temp[i][k]+=a[i][j]*a[j][k])%=P;
  memcpy(a,temp,sizeof(a));
}
inline void MUL(){
  int i;
  R=m,G=(m+1)>>1,B=G,S=R+G+B;
  for(i=1;i<=R;++i){
    a[red(i)][red(i)]=24;
    if(i!=1)a[red(i)][red(i-1)]=1;
    else a[red(i)][green(1)]=1;
  }
  for(i=1;i<=G;++i){
    a[green(i)][green(i)]=25;
    a[green(i)][blue(i)]=1;
    if(i!=G)a[green(i)][green(i+1)]=1;
  }
  for(i=1;i<=B;++i)
    a[blue(i)][blue(i)]=26;
  for(i=1;i<=S;++i)b[i][i]=1;
  int mi=n>>1;
  while(mi){
    if(mi&1)Multi1();
    Multi2(),mi>>=1;
  }
}
inline void CAL(){
  int ans=0,i,j;
  for(i=0;i<=R;++i)
    for(j=1;j<=B;++j)
      if(f[i][j][1])
        (ans+=f[i][j][1]*b[i==0?green(1):red(i)][blue(j)])%=P;
  if(n&1)
    for(i=0;i<=R;++i)
      for(j=1;j<=G;++j)
        if(f[i][j][0])
          (ans+=f[i][j][0]*b[i==0?green(1):red(i)][green(j)])%=P;
  printf("%d\n",ans);
}
int main(){
  scanf("%s%d",s+1,&n);
  m=strlen(s+1),n+=m;
  DP(),MUL(),CAL();
  return 0;
}

原文地址:https://www.cnblogs.com/TSHugh/p/8470391.html

时间: 2024-10-08 08:13:31

【Codeforces 506E】Mr.Kitayuta’s Gift&&【BZOJ 4214】黄昏下的礼物 dp转有限状态自动机+矩阵乘法优化的相关文章

Codeforces 506E Mr. Kitayuta&#39;s Gift (矩阵乘法,动态规划)

描述: 给出一个单词,在单词中插入若干字符使其为回文串,求回文串的个数(|s|<=200,n<=10^9) 这道题超神奇,不可多得的一道好题 首先可以搞出一个dp[l][r][i]表示回文串左边i位匹配到第l位,右边i位匹配到第r位的状态数,可以发现可以用矩阵乘法优化(某人说看到n这么大就一定是矩阵乘法了= =) 但这样一共有|s|^2个节点,时间复杂度无法承受 我们先把状态树画出来:例如add 可以发现是个DAG 我们考虑把单独的每条链拿出来求解,那么最多会有|s|条不同的链,链长最多为|s

Codeforces 506E Mr. Kitayuta&#39;s Gift - 动态规划 - 矩阵

题目传送门 通往Codeforces的航线 通往vjudge的航线 题目大意 给定一个仅包含小写字母的串$s$,要求插入恰好$n$个小写字母字符,使得新串为回文串.问这样得到的新串有多少个是本质不同回文串. $1\leqslant |s| \leqslant 200,1 \leqslant n \leqslant 10^{9} $ 神题orz...orz...orz.Petr orz...orz...orz. 好了开始扯正题了. 任意位置插入并不太好处理.可以转化一下这个问题,求一个长度为$|s

codeforces 505A. Mr. Kitayuta&#39;s Gift 解题报告

题目链接:http://codeforces.com/problemset/problem/505/A 题目意思:给出一个长度不大于10的小写英文字符串 s,问是否能通过在字符串的某个位置插入一个字母,使得新得到的字符串成为回文串. /**************************************(又到自我反省时刻) 做的时候,通过添加一个单位使得长度增加1,找出中点,检验前一半的位置,找出对称位置替换成对应的前一半位置的字符,然后原字符串剩下的部分追加到后面,再判断回文.但是由于

[BZOJ 1009] [HNOI2008] GT考试 【AC自动机 + 矩阵乘法优化DP】

题目链接:BZOJ - 1009 题目分析 题目要求求出不包含给定字符串的长度为 n 的字符串的数量. 既然这样,应该就是 KMP + DP ,用 f[i][j] 表示长度为 i ,匹配到模式串第 j 位的字符串个数,然后转移就是可以从第 j 位加上一个字符转移到另一个位置. 然而..我并没有写过KMP + DP,我觉得还是写AC自动机+DP比较简单..于是,尽管只有一个模式串,我还是写了AC自动机+DP. 然后就是建出AC自动机,f[i][j] 表示长度为 i ,走到节点 j 的字符串的个数.

codeforces Round 286# problem A. Mr. Kitayuta&#39;s Gift

Mr. Kitayuta has kindly given you a string s consisting of lowercase English letters. You are asked to insert exactly one lowercase English letter into s to make it a palindrome. A palindrome is a string that reads the same forward and backward. For

Codeforces 505 A Mr. Kitayuta&#39;s Gift【暴力】

题意:给出一个字符串,可以向该字符串的任意位置插入一个字母使其变成回文串 因为字符串的长度小于10,枚举插入的字母,和要插入的位置,再判断是否已经满足回文串 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include <cmath> 5 #include<stack> 6 #include<vector> 7 #include<map> 8

Codeforces Round #286 (Div. 2)A. Mr. Kitayuta&#39;s Gift(暴力,string的应用)

由于字符串的长度很短,所以就暴力枚举每一个空每一个字母,出现行的就输出.这么简单的思路我居然没想到,临场想了很多,以为有什么技巧,越想越迷...是思维方式有问题,遇到问题先分析最简单粗暴的办法,然后一步一步的优化,不能盲目的想. 这道题要AC的快需要熟悉string的各种用法.这里做个简单总结:C++中string的常见用法. #include<iostream> #include<cstdio> #include<cstdlib> #include<cstrin

水题 Codeforces Round #286 (Div. 2) A Mr. Kitayuta&#39;s Gift

题目传送门 1 /* 2 水题:vector容器实现插入操作,暴力进行判断是否为回文串 3 */ 4 #include <cstdio> 5 #include <iostream> 6 #include <algorithm> 7 #include <cstring> 8 #include <string> 9 #include <vector> 10 using namespace std; 11 12 const int MAXN

Mr. Kitayuta&#39;s Gift

1 //回文判断 Codeforces Round #286 (Div. 2) 2 #include<iostream> 3 #include<cstdio> 4 int main() 5 { 6 string f,temp; cin>>f; 7 int len(f.size()); 8 for(char c ='a';c<='z';c++) 9 { 10 temp.clear(); 11 temp.push_back(c); 12 for(int i=0;i&l