Codeforces 506E Mr. Kitayuta's Gift (矩阵乘法,动态规划)

描述:

给出一个单词,在单词中插入若干字符使其为回文串,求回文串的个数(|s|<=200,n<=10^9)

这道题超神奇,不可多得的一道好题

首先可以搞出一个dp[l][r][i]表示回文串左边i位匹配到第l位,右边i位匹配到第r位的状态数,可以发现可以用矩阵乘法优化(某人说看到n这么大就一定是矩阵乘法了= =)

但这样一共有|s|^2个节点,时间复杂度无法承受

我们先把状态树画出来:例如add

可以发现是个DAG

我们考虑把单独的每条链拿出来求解,那么最多会有|s|条不同的链,链长最多为|s|,时间复杂度为O(|s|^4log n)还是得跪

好像没什么思路了对吧= =(我第一步转化就没想到了= =)

我们考虑记有24个自环的为n24,25个自环的为n25,可以发现n24+n25*2=|s|或|s|+1也就是说对于一个确定的n24,一定有一个确定的n25

那么这样构图:

可以发现所有状况都被包括进来了!!!

那么一共有2|s|个节点,时间复杂度降了一个|s|,看上去好像还是不行

压常数= =

可以发现这个是棵树,也就是说如果按拓扑序编号的话,到时的矩阵左下角将是什么都没有的

那么就直接for i = 1 to n j = i to n k=i to j 就行了 = =

总结下吧

这道题为何神奇呢

首先它把一个DAG的图拆成了若干条相似的链

然后它又把这些链和成了一个更和谐的图

最后再观察题目性质得到一个比较神奇的优化方法

这给了我们什么启迪呢= =

首先遇到某些DAG我们可以考虑拆成若干条相似的链

遇到某些链我们可以考虑把他们合成一个图

最重要的是,还是得参透题目的性质

这道题基本都是依靠题目的性质到达下一步的,只有真正读懂读透这道题,我们才能想出更好的解法

CODE:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 410
#define mod 10007
typedef int ll;
struct marix{
    int r,c;ll a[maxn][maxn];
    inline void init(int x){r=c=x;for (int i=1;i<=x;i++) a[i][i]=1;}
}x,y;
inline void muti(marix &ans,const marix x,const marix y){
    ans.r=ans.c=x.r;
    for (int i=1;i<=x.r;i++)
        for (int j=i;j<=y.c;j++) {
            int tmp=0;
            for (int k=i;k<=j;k++)
                (tmp+=x.a[i][k]*y.a[k][j])%=mod;
            ans.a[i][j]=tmp;
        }
}
inline void power(marix &ans,marix x,int y) {
    ans.init(x.r);
    for (;y;y>>=1) {
        if (y&1) muti(ans,ans,x);
        muti(x,x,x);
    }
}
ll f[210][210][210];
char s[maxn];
inline ll calc(int l,int r,int x) {
    ll &u=f[x][l][r];
    if (u!=-1) return u;
    u=0;
    if (l==r) return u=x==0;
    if (s[l]==s[r]) {
        if (l+1==r) return u=x==0;
        return u=calc(l+1,r-1,x);
    }
    if (x>0) return u=(calc(l+1,r,x-1)+calc(l,r-1,x-1))%mod;
    return u;
}
int main(){
    int n,m;
    memset(f,-1,sizeof(f));
    scanf("%s",s+1);
    scanf("%d",&n);
    m=strlen(s+1);
    n+=m;
    int l=(n+1)/2,n24=m-1,n25=(m+1)/2,n26=n25;
    x.r=x.c=n24+n25+n26;
    for (int i=1;i<=n24;i++) x.a[i][i]=24,x.a[i][i+1]=1;
    for (int i=n24+1;i<=n25+n24;i++) x.a[i][i]=25,x.a[i][i+n25]=1;
    for (int i=n24+n25+1;i<=n25+n24+n26;i++) x.a[i][i]=26;
    for (int i=n24+1;i<n25+n24;i++) x.a[i][i+1]=1;
    marix y;
    power(y,x,l-1);
    muti(x,y,x);
    ll ans;
    for (int i=0;i<=n24;i++) {
        int j=(m-i+1)/2,k=l-i-j;
        if (k<0) continue;
        ll sum=calc(1,m,i);
        (ans+=sum*x.a[n24-i+1][n24+j+n25]%mod)%=mod;
        if ((n&1)&&(m-i&1^1))
            (ans=ans-sum*y.a[n24-i+1][n24+j]%mod+mod)%=mod;
    }
    printf("%d\n",ans);
    return 0;
}

Codeforces 506E Mr. Kitayuta's Gift (矩阵乘法,动态规划)

时间: 2024-10-06 03:23:24

Codeforces 506E Mr. Kitayuta's Gift (矩阵乘法,动态规划)的相关文章

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,找出中点,检验前一半的位置,找出对称位置替换成对应的前一半位置的字符,然后原字符串剩下的部分追加到后面,再判断回文.但是由于

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

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}

CF506E Mr. Kitayuta&#39;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\)完全匹配上的方案数. 那么分情况讨

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