1. 程式人生 > >哈爾濱理工大學第七屆程序設計競賽初賽(高年級組)E - 音樂轉換

哈爾濱理工大學第七屆程序設計競賽初賽(高年級組)E - 音樂轉換

con 難點 desc 哈爾濱 最大 需要 ont 字符串長度 直接

題目描述

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\n", 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\n", C.a[0][C.c - 1]);
  }
  return 0;
}

  

哈爾濱理工大學第七屆程序設計競賽初賽(高年級組)E - 音樂轉換