uva 12045 (矩阵快速幂)

3 篇文章 0 订阅
2 篇文章 0 订阅

题意:

一个长度为正数的字符串,只包含“a"和"b"两种字符。

每次操作,把所有的b变成ab,a变成b。

如S(1) = ab, 则S(2)=b(ab) =bab 

       记L(n)为第n个串的长度。

      给出L(n) = X , L(m) = Y, L(k)

 

做法:

设S(n)中字符a、b的个数分别为a(n)、b(n),则L(n)= a(n) + b(n)

很明显有 a(n+1)=b(n),b(n+1) = a(n)+b(n)

即满足斐波那契数列的性质。

于是可以通过构造转移矩阵,利用快速幂,来得到任意两项之间的关系。

不妨设n < m,假设得到的矩阵为

 |a b|

 |c d|

则有 a * a(n) + b * b(n) = a(m)

c * a(n) + d * b(n) = b(m)

a(n) + b(n) = L(n) = X

a(m) + b(m) = L(m) = Y

于是可解出a(n), b(n) ,若小于0 说明字符个数小于0,明显不合题意。

类似可算出a(1),b(1),同样必须保证大于等于0

最后再推出k的情况即可。

 

 

#include <cstdio>
#include <iostream>
using namespace std;
const int MOD = 1000000007, INF = 1e9;
typedef long long ll;
const int N = 2;
struct Mat {
    ll mat[N][N];
    void init() {
        for(int i = 0; i < N; i++)
            for(int j = 0; j < N; j++)
                mat[i][j] = i || j ? 1 : 0;
    }
    void init2(int x) {
        for(int i = 0; i < N; i++)
            for(int j = 0; j < N; j++)
                mat[i][j] = i == j ? x : 0;
    }
    Mat operator * (const Mat &ano) {
        Mat res;
        res.init2(0);
        for(int i = 0; i < N; i++)
            for(int j = 0; j < N; j++)
                for(int k = 0; k < N; k++) {
                    res.mat[i][j] += mat[i][k] * ano.mat[k][j];
                    res.mat[i][j] %= MOD;
                }
        return res;
    }
};
Mat quickPow(Mat A, int n) {
    Mat res;
    res.init2(1);
    while(n) {
        if(n & 1)
            res = res * A;
        A = A * A;
        n >>= 1;
    }
    return res;
}
// a * x + b * y = s1
// c * x + d * y = s2
void fun(ll a, ll b, ll s1,
         ll c, ll d, ll s2, ll &x, ll &y) {
    x = y = -1;
    if(b * c != d * a) {
        y = (a * s2 - c * s1) / (a * d - b * c);
        if(a)
            x = (s1 - b * y) / a;
        else if(c)
            x = (s2 - d * y) / c;
        if(a * x + b * y != s1 ||
           c * x + d * y != s2)
           x = y = -1;
    }
}
int main() {
//    freopen("in.txt", "r", stdin);
    int t, n, x, m, y, k;
    ll ans;
    Mat A;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d%d%d%d", &n, &x, &m, &y, &k);
        if(n > m) {
            swap(n, m);
            swap(x, y);
        }

        ll an, bn;
        A.init();
        A = quickPow(A, m - n);
        fun(1, 1, x, A.mat[0][0] + A.mat[1][0],
            A.mat[0][1] + A.mat[1][1], y, an, bn);
        if(an < 0 || bn < 0) {
            puts("Impossible");
            continue;
        }

        ll a1, b1;
        A.init();
        A = quickPow(A, n-1);
        fun(A.mat[0][0], A.mat[0][1], an,
            A.mat[1][0], A.mat[1][1], bn,
            a1, b1);
        if(a1 < 0 || b1 < 0){
            puts("Impossible");
            continue;
        }

        A.init();
        ll ak, bk;
        if(k >= n) {
            A = quickPow(A, k - n);
            ak = an * ((A.mat[0][0] + A.mat[1][0]) % MOD);
            bk = bn * ((A.mat[0][1] + A.mat[1][1]) % MOD);
            ak %= MOD, bk %= MOD;
            ans = ak + bk;
        } else {
            A = quickPow(A, n - k);
            fun(A.mat[0][0], A.mat[0][1], an,
                A.mat[1][0], A.mat[1][1], bn,
                ak, bk);
            if(ak < 0 || bk < 0) {
                puts("Impossible");
                continue;
            }
            ak %= MOD, bk %= MOD;
            ans = ak + bk;
        }
        ans %= MOD;
        printf("%d\n", (int)ans);
    }
    return 0;
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值