zoukankan      html  css  js  c++  java
  • CF506E Mr. Kitayuta's Gift

    Link

    Solution

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

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

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

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

    • (s_l eq s_r)

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

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

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

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

    • (s_l=s_r)

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

      • (r-l+1le 2)

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

        [25 imes 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 imes f_{x,l,r}->f_{x+1,l,r} ]

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

    [26 imes 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个自环。如下图:

    GL3gfA.png

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

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

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

    GLGaqK.png

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

    这张图中,每个点代表的不再是((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|)为奇数的情况。先按照偶数的情况算出来之后去掉不合法的即可。

    总长度是奇数的话,如果整条长为(lceilfrac{n+|s|}{2} ceil)的路径,最后一步恰好是从原图的一个((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]," 
    "[i==m]);fprintf(stderr,"
    
    ");}
    };
    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
    ",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
    ",res),0;
    }
    
    /*
      0:S
      1~len-1:n24
      len~m-1:n25
      m:T
     */
    
    
  • 相关阅读:
    一些文件的扩展名
    关于git,从svn转到git
    trousers--------dpkg: 处理软件包 trousers (--configure)时报错
    Ubuntu下运行DrClient以上网
    Ubuntu下的终端命令--复制文件从一个文件夹到另一个文件夹下
    VSCode放大字体的快捷键
    opessl版本过低造成的函数使用错误
    python的基本语法
    ubuntu和windows的解码方式
    ubuntu下强制删除文件夹
  • 原文地址:https://www.cnblogs.com/fruitea/p/12683328.html
Copyright © 2011-2022 走看看