CF506E Mr. Kitayuta's Gift

Link

Solution

题意转化之后就是求有多少个长度是\(n+|s|\)的回文串,\(s\)是它的子序列。

先考虑\(n+|s|\)为偶数的情况。

可以大力dp计数,设\(f_{x,l,r}\)表示填了前\(x\)个和后\(x\)个字符,在能匹配就匹配的的情况下,还剩\(s[l\dots r]\)这段区间没有匹配上的方案数。注意能匹配就匹配,这个限制可以保证不会算重。另外设\(g_x\)表示在填了前\(x\)个和后\(x\)个字符之后,已经可以和\(s\)完全匹配上的方案数。

那么分情况讨论一下\(f\)的转移:

  • \(s_l\neq s_r\)

    \[f_{x,l,r}->f_{x+1,l+1,r}
    \]

    \[f_{x,l,r}->f_{x+1,l,r-1}
    \]

    \[24\times f_{x,l,r}->f_{x+1,l,r}
    \]

    这个情况下填第\(x+1\)个字符,最多只能匹配\(s_l,s_r\)中的一个。

  • \(s_l=s_r\)

    这个时候要看能否一步到位,把\(s\)给匹配完

    • \(r-l+1\le 2\)

      \[f_{x,l,r}->g_{x+1}
      \]

      \[25\times f_{x,l,r}->f_{x+1,l,r}
      \]

    • \(r-l+1> 2\)

      \[f_{x,l,r}->f_{x+1,l+1,r-1}
      \]

      \[25\times f_{x,l,r}->f_{x+1,l,r}
      \]

另外还有\(g\)自己的转移:

\[26\times g_x->g_{x+1}
\]

答案就是\(g_{(n+|s|)/2}\)。加上矩阵快速幂加速复杂度可以达到\(O(|s|^6log(n))\)。

继续考虑优化。

上面的\(dp\)其实给了一定的提示,\(f\)的第一维都是从\(x\)转移到\(x+1\),\((l,r)\)两维则是转移给\((l,r)\)自己,以及视\(s_l,s_r\)的异同情况转移给\((l+1,r-1)\)或者\((l+1,r)/(l,r-1)\)。

抛掉第一维,考虑对所有的\((l,r)\)建点。如果\(s_l=s_r\),就向自己连25个自环,向点\((l+1,r-1)\)连1条有向边,或者向点\(g\)连一条边权为1的有向边。否则,就向自己连24个自环,分别向点\((l+1,r)\)和\((l,r-1)\)连1条有向边。最后点\(g\)向自己连26个自环。如下图:

图中绿点就是\(s_l=s_r\)的状态,红点就是\(s_l\neq s_r\)的状态。

那么现在问题就转化成了\(dag\)上确定长度( \(\frac{n+|s|}{2}\))的路径计数。

进一步发现,对于一条路径,不考虑上面的自环,当红点数量为\(k\)时,绿点的数量就是\(\lceil\frac{|s|-k}{2}\rceil\),且指向终点的点一定是绿点。那么交换任意两点的位置,对答案的贡献是不变的(不考虑自环)。那么本质不同的路径就只有\(O(|s|)\)条。我们把这些路径上的点按颜色排列,红点在前,绿点在后,进一步优化建图以后就只剩下\(O(|s|)\)个点。见下(来自https://www.cnblogs.com/CQzhangyu/p/8685601.html):

?

图中没有把起点画出来,起点应该是连接最左侧的红点和绿点,因为可以一个红点也不经过。

这张图中,每个点代表的不再是\((l,r)\)这一个状态,变成了大概\(len=r-l+1\)这个等价类(绿点代表两个\(len\)),红点出发的边使\(len-1\),绿点出发的边使\(len-2\)。

图上有一类边我们还不知道数量:红点到绿点的边。这类边上就存储了所有有这么多个红点的路径数量。

考虑设\(cnt_{x,l,r}\)表示原图从起点出发到\((l,r)\)节点,经过了\(x\)个红点的路径数量。使用记忆化搜索\(O(|s|^3)\)可以得到。

那么现在的图上红点到绿点的连边,假设这是条经过了\(k\)个红点的路径,那么数量就是\(\sum cnt_{k,i,i}+[s_i==s_{i+1}]cnt_{k,i,i+1}\)。

这个时候使用矩阵快速幂加速,复杂度就降为\(O(|s^3|log(n))\)。

构建转移矩阵时会发现是个上三角矩阵,做乘法的时候约束一下\(for\)循环范围常数变成原来的\(\frac{1}{6}\),可以通过此题。

还剩下\(n+|s|\)为奇数的情况。先按照偶数的情况算出来之后去掉不合法的即可。

总长度是奇数的话,如果整条长为\(\lceil\frac{n+|s|}{2}\rceil\)的路径,最后一步恰好是从原图的一个\((i,i+1)\)点走到终点的话,那么它就不合法,因为现在最后一步只能填一个字符了。

所以对这种路径计数就好了。把图上红点到绿点\(g_{k,i,i}\)的边去掉,再把终点的自环去掉,再跑一遍就是想要减掉的东西。

Code

#include<bits/stdc++.h>
using namespace std;
#define REP(i,a,b) for(int i=(a),_ed=(b);i<=_ed;++i)
#define DREP(i,a,b) for(int i=(a),_ed=(b);i>=_ed;--i)
#define mp(x,y) make_pair((x),(y))
#define sz(x) (int)(x).size()
#define pb push_back
typedef long long ll;
typedef pair<int,int> pii;
inline int read(){
    register int x=0,f=1;register char ch=getchar();
    while(!isdigit(ch)){if(ch==‘-‘)f=0;ch=getchar();}
    while(isdigit(ch)){x=x*10+(ch^‘0‘);ch=getchar();}
    return f?x:-x;
}

const int N=3e2+5,mod=1e4+7;
int n,len,m,vis[N][N][N],cnt[N][N][N];
char s[N];
inline void inc(int& x,int y){x=x+y<mod?x+y:x+y-mod;}
inline void dec(int& x,int y){x=x-y>=0?x-y:x-y+mod;}
struct matrix{
    int x[N][N],flg;
    inline matrix(int _flg=0):flg(_flg){memset(x,0,sizeof x);}
    inline int* operator[](int p){return x[p];}
    inline const int* operator[](int p)const{return x[p];}
    inline matrix operator*(const matrix& o){
	matrix ans;
	if(!flg)REP(i,1,m)REP(k,i,m)REP(j,k,m)inc(ans[i][j],1ll*x[i][k]*o[k][j]%mod);
	else REP(i,1,m)REP(j,1,m)inc(ans[1][j],1ll*x[1][i]*o[i][j]%mod);
	return ans;
    }
    inline void print(){REP(i,1,m)fprintf(stderr,"%d%c",x[i][200]," \n"[i==m]);fprintf(stderr,"\n\n");}
};
void ksm(matrix& ans,matrix& b,int n){
    //b.print();
    for(;n;n>>=1,b=b*b)if(n&1)ans=ans*b;
}

inline int dfs(int i,int l,int r){
    if(i<0)return 0;
    if(vis[i][l][r])return cnt[i][l][r];
    vis[i][l][r]=1;
    if(l==1&&r==len)return cnt[i][l][r]=!i;
    if(1<l&&r<len&&s[l-1]==s[r+1])inc(cnt[i][l][r],dfs(i,l-1,r+1));
    if(1<l&&s[l-1]!=s[r])inc(cnt[i][l][r],dfs(i-1,l-1,r));
    if(r<len&&s[l]!=s[r+1])inc(cnt[i][l][r],dfs(i-1,l,r+1));
    return cnt[i][l][r];
}

int main(){
    // freopen("in.in","r",stdin);freopen("imafool.err","w",stderr);
    scanf("%s",s+1);len=strlen(s+1),m=len+(len+1)/2;
    n=read();
    matrix f(1),g;
    REP(t,0,len-1){
	int c=0;
	REP(i,1,len)inc(c,dfs(t,i,i)),(i<len&&s[i]==s[i+1])?inc(c,dfs(t,i,i+1)):void();
	if(!t){
	    f[1][1]=1,f[1][len]=c;
	    g[m][m]=26;
	    REP(i,len,m-1)g[i][i+1]=1,g[i][i]=25;
	}
	else{
	    g[t][m-(len-t+1)/2]=c,g[t][t]=24;
	    if(t<len-1)g[t][t+1]=1;
	}
    }
    ksm(f,g,(n+len+1)/2);
    if(~(n+len)&1)return printf("%d\n",f[1][m]),0;
    int res=f[1][m];
    f=matrix(1),g=matrix();
    REP(t,0,len-1){
	int c=0;
	REP(i,1,len-1)if(s[i]==s[i+1])inc(c,dfs(t,i,i+1));
	if(!t){
	    f[1][1]=1,f[1][len]=c;
	    REP(i,len,m-1)g[i][i+1]=1,g[i][i]=25;
	}
	else{
	    g[t][m-(len-t+1)/2]=c,g[t][t]=24;
	    if(t<len-1)g[t][t+1]=1;
	}
    }
    ksm(f,g,(n+len+1)/2);
    dec(res,f[1][m]);
    return printf("%d\n",res),0;
}

/*
  0:S
  1~len-1:n24
  len~m-1:n25
  m:T
 */

CF506E Mr. Kitayuta's Gift

原文地址:https://www.cnblogs.com/fruitea/p/12683328.html

时间: 2024-11-09 02:07:48

CF506E Mr. Kitayuta's Gift的相关文章

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

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

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

286DIV1E. Mr. Kitayuta&#39;s Gift

题目大意 给定一个由小写字母构成的字符串$s$,要求添加$n(n\le 10^9)$个小写字母,求构成回文串的数目. 简要题解 $n$辣么大,显然要矩阵快速幂嘛. 考虑从两端开始构造以s ss为子串的回文串,该回文串长度为$N=n+s$,每次添加相同的字符,则需要$(N+1)/2$次,则用dp来计算并使用矩阵乘法来优化转移会得到一个$O(|s|^6\log N)$的算法,显然是不可接受的. 考虑这个dp做法,设$f[i][j][k]$表示从两端添加了$k$次字符,原来的$s$的子串$s_{ij}

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

CF 505A Mr. Kitayuta&#39;s Gift

题意 在一个字符串中插入一个字母使其变成一个回文串 可以的话输出这个回文串 否则NA 大水题 插入情况最多就26*11种 可以直接暴力 #include #include using namespace std; const int N = 20; char s[N], p[N]; int l; bool ispal() { for(int i = 0; i < (l + 1) / 2; ++i) if(p[i] != p[l - i]) return false; return true; }

CF 505A Mr. Kitayuta&#39;s Gift(暴力)

题意  在一个字符串中插入一个字母使其变成一个回文串  可以的话输出这个回文串  否则NA 大水题  插入情况最多就26*11种  可以直接暴力 #include<cstdio> #include<cstring> using namespace std; const int N = 20; char s[N], p[N]; int l; bool ispal() { for(int i = 0; i < (l + 1) / 2; ++i) if(p[i] != p[l -

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

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

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

水题 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