zoukankan      html  css  js  c++  java
  • 哈尔滨理工大学第七届程序设计竞赛初赛(高年级组)E

    题目描述

    John是一位热衷的音游爱好者。

    一天,他正在玩音游,碰到了一个难题。他面前有一个初始旋律,游戏目标是要把这一段初始旋律变成目标旋律。但是,难点在于,John并不能随意的改变这一旋律,每一次改变之后的旋律必须在他的旋律库中(或者为目标旋律),更令人头疼的是,每一次操作都需要消耗一定的combo,我们假设从A旋律改变成B旋律,那么这一次操作所消耗的Combo数是A与B的最大子旋律长度(最大公共子序列)再加1。需要注意的是,初始旋律必须变成旋律库中的一个旋律或者直接变成目标旋律,而旋律库中的任意一个旋律可以变成旋律库中的另外一个旋律,或者直接变成目标旋律,也可以变成初始旋律。而目标旋律则不能再进行任何操作。

     

    John想问问你,在不限操作次数的情况下,一共有多少种方案使得初始旋律变成目标旋律,且一共消耗的combo数恰好为T,当然这个答案太大了,你只需要将它对1,000,000,007取余数就可以了。

    请注意,再本题中,所有的旋律你都可以视为一个字符串。  

    输入描述:

    输入第一行有两个正整数,分别为n和T,其中n代表曲库中的旋律数,T代表John所希望消耗的Combo数。
    第二行有一个字符串,代表初始旋律。
    第三行有一个字符串,代表目标旋律。
    接下来N行,每行有一个字符串,代表旋律库中的旋律。
    1<=N<=25
    1<=T<=10^9
    任意出现的一个字符串长度<=25
    任意两个字符串的最长公共子序列的长度<=5
    输入的n+2的字符串两两不同,且均由小写字母组成。

    输出描述:

    输出只有一个正整数,代表方案数对1,000,000,007的余数。
    示例1

    输入

    2 4
    ab
    ba
    bb
    aa

    输出

    2

    题解

    $dp$,矩阵快速幂。

    很容易想到一个$dp$,$dp[i][j]$表示最后变到第$i$个字符串,费用和为$j$的方案数。转移就是$dp[i][j] = sum{dp[k][j - cost[k][i]]}$。

    由于第二维太大了,所以需要优化。

    可以这样想,

    $(dp[0,0],...,dp[0,5],dp[1,0],...,dp[1,5],dp[2,0],...,dp[2,5],......,dp[n + 1,0],...,dp[n + 1,5])$这个矩阵,乘上$A$矩阵

    会变成$(dp[0,1],...,dp[0,6],dp[1,1],...,dp[1,6],dp[2,1],...,dp[2,6],......,dp[n + 1,1],...,dp[n + 1,6])$。

    $A$矩阵根据$dp$转移方程很容易构造。每次乘一个$A$矩阵,$dp$就往后挪了一位,因此拿矩阵快速幂优化即可。

    #include <bits/stdc++.h>
    using namespace std;
    
    const long long mod = 1e9 + 7;
    const int maxn = 30;
    char s[maxn][maxn];
    int len[maxn];
    int c[maxn][maxn];
    int f[maxn][maxn];
    int n, T;
    long long dp[maxn][maxn];
    
    struct M {
      int r, c;
      long long a[200][200];
    };
    
    M mul(const M &a, const M &b) {
      M res;
      res.r = a.r;
      res.c = b.c;
      for(int i = 0; i < res.r; i ++) {
        for(int j = 0; j < res.c; j ++) {
          res.a[i][j] = 0;
        }
      }
      for(int j = 0; j < res.c; j ++) {
        for(int k = 0; k < a.c ; k ++) {
          if(b.a[k][j] == 0) continue;
          for(int i = 0; i < res.r; i ++) {
            long long u = a.a[i][k] * b.a[k][j] % mod;
            res.a[i][j] = (res.a[i][j] + u) % mod;
          }
        }
      }
      return res;
    }
    
    int work(int x, int y) {
      for(int i = 0; i <= len[x]; i ++) {
        for(int j = 0; j <= len[y]; j ++) {
          f[i][j] = 0;
        }
      }
      for(int i = 1; i <= len[x]; i ++) {
        for(int j = 1; j <= len[y]; j ++) {
          f[i][j] = max(f[i- 1][j], f[i][j - 1]);
          if(s[x][i - 1] == s[y][j - 1]) {
            f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
          }
        }
      }
      return f[len[x]][len[y]] + 1;
    }
    
    int main() {
      while(~scanf("%d%d", &n, &T)) {
        scanf("%s", s[0]);
        scanf("%s", s[n + 1]);
        for(int i = 1; i <= n; i ++) {
          scanf("%s", s[i]);
        }
        for(int i = 0; i <= n + 1; i ++) {
          len[i] = strlen(s[i]);
        }
    
        for(int i = 0; i <= n + 1; i ++) {
          for(int j = 0; j <= n + 1; j ++) {
            c[i][j] = -1;
          }
        }
        for(int i = 0; i <= n; i ++) {
          for(int j = 0; j <= n; j ++) {
            if(i == j) continue;
            c[i][j] = work(i, j);
          }
        }
        for(int j = 0; j <= n; j ++) {
          c[j][n + 1] = work(j, n + 1);
        }
    
        for(int i = 0; i <= n + 1; i ++) {
          for(int j = 0; j <= 10; j ++) {
            dp[i][j] = 0;
          }
        }
        dp[0][0] = 1;
        for(int j = 1; j <= 20; j ++) {
          for(int i = 0; i <= n + 1; i ++) {
            for(int k = 0; k <= n + 1; k ++) {
              if(c[k][i] == -1) continue;
              if(j - c[k][i] >= 0) {
                dp[i][j] = (dp[i][j] + dp[k][j - c[k][i]]) % mod;
              }
            }
          }
        }
    
        if(T <= 10) {
          printf("%lld
    ", dp[n + 1][T]);
          continue;
        }
    
        M A;
        A.r = 6 * (n + 2);
        A.c = 6 * (n + 2);
        for(int i = 0; i < A.r; i ++) {
          for(int j = 0; j < A.c; j ++) {
            A.a[i][j] = 0;
            if(i == j) A.a[i][j] = 1;
          }
        }
    
        M B;
        B.r = 6 * (n + 2);
        B.c = 6 * (n + 2);
        for(int i = 0; i < B.r; i ++) {
          for(int j = 0; j < B.c; j ++) {
            B.a[i][j] = 0;
          }
        }
    
        for(int j = 0; j < B.c; j ++) {
          if(j % 6 != 5) {
            B.a[j + 1][j] = 1;
          } else {
            for(int k = 0; k <= n + 1; k ++) {
              if(c[k][j / 6] == -1) continue;
              B.a[k * 6 + 6 - c[k][j / 6]][j] = 1;
            }
          }
        }
    
        M C;
        C.r = 1;
        C.c = 6 * (n + 2);
        for(int j = 0; j < C.c; j ++) {
          C.a[0][j] = dp[j / 6][j % 6];
        }
    
        int p = T - 5;
        while(p) {
          if(p & 1) A = mul(A, B);
          B = mul(B, B);
          p = p / 2;
        }
        C = mul(C, A);
        printf("%lld
    ", C.a[0][C.c - 1]);
      }
      return 0;
    }
    

      

  • 相关阅读:
    CTO这点事(技术,业务,管理,情商,周期,趋势)转
    Unit Test相关问题汇总
    优秀的命令行文件传输程序(转)
    ZOJ3329之经典概率DP
    Sql Server 2005 开发版亲測可用下载地址
    Hadoop与HBase中遇到的问题
    Struts2自己定义拦截器实例—登陆权限验证
    手游Apk破解疯狂,爱加密apk加固保护开发人员
    HighCharts 具体使用及API文档说明
    Linux(SLES)挂载NTFS移动硬盘实践
  • 原文地址:https://www.cnblogs.com/zufezzt/p/8086034.html
Copyright © 2011-2022 走看看