哈尔滨理工大学第七届程序设计竞赛初赛(高年级组)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<=251<=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;
}

  

时间: 2024-10-09 03:34:35

哈尔滨理工大学第七届程序设计竞赛初赛(高年级组)E - 音乐转换的相关文章

牛客网 哈尔滨理工大学第七届程序设计竞赛决赛(网络赛-低年级组)求最大值

题目描述 给出一个序列,你的任务是求序列中 (a[j]-a[i])/(j-i)[1<=i<j<=n]的最大值 输入描述: 本题包含多组输入,每组输入第一行一个数字n,表示序列的长度.然后接下来一行输入n个数,表示原先序列的样子.数据范围:3<=n<=200000-1000000000<=a[i]<=1000000000 输出描述: 每组数据输出一行一个浮点数,保留两位小数,表示所求的最大值. 示例1 输入 5 2 4 6 8 10 输出 2.00 备注: 输入只有

哈尔滨理工大学第七届程序设计竞赛决赛(网络赛-高年级组)A - 所有情况的和

题目描述 在acimo星球, tabris 是一名勇敢的屠龙勇士,在上绿岛屠龙前决定挑选N种装备武装自己,现在每种装备有两个,**但每种装备tabris必须选择拿一个**,**不能多也不能少**.每件装备有自己的属性值,能给tabris属性加成.对于不同种类的装备之间有叠加效果,如果选择多件装备,最终的属性加成为他们的乘积.若tabris初始属性值为0,最后属性加成的期望是多少. 输入描述: 有多组测试样例,输入到文件结束.每组测试数据的第一行包含一个正整数NN,表示装备的种类数.接下来N行,每

湖南省第七届程序设计竞赛 最优对称路径

http://acm.nyist.net/JudgeOnline/problem.php?pid=564 湖南省第七届大学生计算机程序设计竞赛 题目G 最优对称路径 给一个n行n列的网格,每个格子里有一个1到9的数字.你需要从左上角走到右下角,其中每一步只能往上.下.左.右四个方向之一走到相邻格子,不能斜着走,也不能走出网格,但可以重复经过一个格子.为了美观,你经过的路径还必须关于"左下-右上"这条对角线对称.下图是一个6x6网格上的对称路径. 你的任务是统计所有合法路径中,数字之和最

哈尔滨理工大学第六届程序设计团队 I-Team

/* 以前做过一个插队的题,这个类似从后往前操作 */ #include <iostream> #include <stdio.h> #include <algorithm> #include <string.h> #include <vector> #define N 600000 using namespace std; struct node { int x,y,z; //每一个操作,z表示这个操作是不是有效的 node(){} node(

哈尔滨理工大学第六届程序设计团队 E-Mod

/* 成功水过,哈哈哈,可能数据水吧 */ #include <stdio.h> #include <algorithm> #include <string.h> #include <queue> #include <set> #define t_mid (l+r >> 1) #define ls (o<<1) #define rs (o<<1 | 1) #define lson ls,l,t_mid #def

哈尔滨理工大学第六届程序设计团队 H-Permutation

/* 数学是硬伤......推了半小时推出来一个错误的公式 */ #include <iostream> #include <stdio.h> #include <algorithm> #include <string.h> #define mod 1000000007 using namespace std; int n,t; /* 推出来的错误公式....... f[n]=f[n-1]+(n-1)*(f[n-1]-1); */ long long a[2

东南大学第十三届程序设计竞赛初赛题解

问题 A: 天梯评分系统 题目描述 在一个下雨的日子,沈学姐和四个好基友约定无事一同打dota(dota是一个5对5的MOBA类游戏)因为想证明谁最NB,他们就全部注册新号去爬天梯了.天梯有一套完整的评分系统,它可以根据每位选手每局的数据进行评分,因为dota的英雄既有辅助又有ganker还有后期,所以不同的英雄的评分标准不一样.可惜那天天梯服务器维护,无法进行评分.于是,他们记录下每一局的数据,找你来帮忙,希望你能够帮他们仿照天梯编一个评分系统,以便于他们比较谁是真正的神牛. 已知对于每个账号

陕西师范大学第七届程序设计竞赛网络同步赛 J 黑猫的小老弟【数论/法拉数列/欧拉函数】

链接:https://www.nowcoder.com/acm/contest/121/J来源:牛客网 题目描述 大家知道,黑猫有很多的迷弟迷妹,当然也有相亲相爱的基友,这其中就有一些二五仔是黑猫的小老弟.小老弟是如何产生的呢?聪明的iko告诉黑猫,其实是有规律的(她怎么知道???)! 一开始,有两个原始二五仔,代号0/1和1/1, 从原始二五仔到第n代小老弟,每代相邻两个小老弟a/b和c/d,产生一个新的小老弟(a+c)/(b+d),成为下一代新成员.将每一代的小老弟代号约分(包括0/1,1/

2018年长沙理工大学第十三届程序设计竞赛 E 小木乃伊到我家

题目描述 AA的欧尼酱qwb是个考古学家,有一天qwb发现了只白白圆圆小小的木乃伊,它是个爱哭鬼却很努力.qwb想把这么可爱的小木乃伊送给AA,于是便找上了快递姐姐,这下可让快递姐姐犯愁了,因为去往AA家的路实在太难走了(甚至有可能没有路能走到AA家),快递姐姐找上聪明的ACMer,想请你帮忙找出最快到达AA家的路,你行吗? 输入描述: 第一行输入两个整数n和m(2<=n<=m<=200000),分别表示有n座城市和m条路,城市编号为1~n(快递姐姐所在城市为1,AA所在城市为n).接下