LG题解 P4910 帕秋莉的手环

更好的阅读体验?

Description

对一个长度为 \(n\) 的环 \(01\) 染色,要求相邻两个数之间必须有一个 \(1\),求方案数。\(T\) 组询问。

Solution

\(f_i\) 表示长度为 \(i\) 的环的方案数。

你发现标签里有斐波那契,然后你准备猜一手结论。

用脚想一想就能得到 \(f_1 = 1\)(只有 \(1\) 这一种方案),\(f_2 = 3\) (有 \(11,01,10\) 这三种方案)。然后你想到了斐波那契的递推公式:

\[f_i = f_{i - 1} + f_{i - 2} \]

你在打草纸上模拟出了 \(f_5 = 11\)

并花了 \(5min\) 用计算器验证了 \(f_9 = 76,f_{20} = 15127\) 的结果。

然后你花 \(10min\) 写了个矩阵快速幂,然后你把它切了。。。

考虑正确性:

考虑第 \(i\) 个位置,如果上个金在 \(i-2\) 的位置出现,你可以在这里放一个金,如果上个金在 \(i-1\) 的位置出现,你也可以在这里放一个金。不难发现只有这两种转移是合法的,前 \(i-1\) 长度的项链的所有合法方案有且仅有这两种情况,所以到当前位置的方案数就是前两个位置的方案数的和。

这个转移比较套路,矩阵快速幂加速转移即可,如果不懂可以来看一下我写的这篇博客矩阵乘法

总复杂度为 \(O(T \times 2^3 \log n)\)

Code

/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define int long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;

struct Matrix {
    int a[2][2];
    Matrix () { memset(a, false, sizeof a); }
    Matrix operator * (Matrix b) {
        Matrix res;
        for(int i = 0; i < 2; ++i) 
            for(int j = 0; j < 2; ++j) 
                for(int k = 0; k < 2; ++k) 
                    res.a[i][j] = (res.a[i][j] + a[i][k] * b.a[k][j] % mod) % mod;
        return res;
    }
    Matrix operator ^ (int p) {
        Matrix res, x = *this;
        res.a[0][0] = res.a[1][1] = 1;
        while(p) {
            if(p & 1) res = res * x;
            x = x * x, p >>= 1;
        }
        return res;
    }
}ans, base;

int read(){
    int s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    return f ? -s : s;
}

void Init() {
    ans.a[0][0] = 3, ans.a[0][1] = 1;
    ans.a[1][0] = 0, ans.a[1][1] = 0;
    base.a[0][0] = 1, base.a[0][1] = 1;
    base.a[1][0] = 1, base.a[1][1] = 0;
}

signed main()
{
    int T = read();
    while(T--) {
        int n = read();
        if(n == 1) puts("1");
        else if(n == 2) puts("3");
        else {
            Init();
            base = base ^ (n - 2);
            ans = ans * base;
            printf("%lld\n", ans.a[0][0]);
        }
    }
    return 0;
}
posted @ 2021-09-09 22:04  Suzt_ilymtics  阅读(56)  评论(1编辑  收藏  举报