1 条题解

  • 0
    @ 2025-8-24 22:05:10

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar liangbowen
    不能再摆了,,,

    搬运于2025-08-24 22:05:10,当前版本为作者最后更新于2022-12-02 18:38:17,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    前言

    题目传送门!

    更好的阅读体验?

    矩阵快速幂优化 DP 经典题。

    前置知识:会做简单的 DP、矩阵加速。

    题目简述

    给定一个长度为 nn 的环,每个位置可以填 1122。相邻两个不能同时填 11

    求方案数。多测。

    朴素 DP

    思路

    看到题目,很容易想到环形 DP。

    dpi,0dp_{i, 0} 表示第 ii 位填 11 的总方案数,dpi,1dp_{i, 1} 表示第 ii 位填 22 的总方案数。

    状态转移方程如下:

    • ii 位填 11,则上一位只能填 22。所以 dpi,0=dpi1,1dp_{i, 0} = dp_{i - 1, 1}
    • ii 位填 22,则上一位可以填 1122。所以 dpi,1=dpi1,0+dpi1,1dp_{i, 1} = dp_{i - 1, 0} + dp_{i - 1, 1}

    初始化要分类讨论一下:

    • 如果第 11 位填 11,则第 nn 位只能填 22。所以答案算上 dpn,1dp_{n, 1}
    • 如果第 11 位填 22,则第 nn 位可以填 1122。所以答案算上 dpn,0+dpn,1dp_{n, 0} + dp_{n, 1}

    把公式抄上去即可。时间复杂度 O(n)O(n)

    代码

    按照题目的应该是可以拿 6060 分的。但是只有 4848 分。

    提交记录

    #include <iostream>
    #include <cstdio>
    using namespace std;
    const int N = 1e6 + 5, mod = 1e9 + 7;
    int n, dp[N][2]; //dp[i][0/1] : 第i位填A或B
    void calc()
    {
    	for (int i = 2; i <= n; i++)
    		dp[i][0] = dp[i - 1][1],                        //第i位填1,则上一位只能填2
    		dp[i][1] = (dp[i - 1][0] + dp[i - 1][1]) % mod; //第i位填2,则上一位可以填1或2
    }
    void solve()
    {
    	long long ans = 0;
    	scanf("%d", &n);
    
    	dp[1][0] = 1, dp[1][1] = 0;           //第一位填1,
    	calc(), ans = (ans + dp[n][1]) % mod; //则最后一位只能填2
    
    	dp[1][0] = 0, dp[1][1] = 1;                      //第一位填2,
    	calc(), ans = (ans + dp[n][0] + dp[n][1]) % mod; //则最后一位可以填1或2
    
    	cout << ans << '\n';
    }
    int main()
    {
    	ios::sync_with_stdio(false);
    	int T;
    	scanf("%d", &T);
    	while (T--) solve();
    	return 0;
    }
    

    矩阵加速

    思路

    如果 nn10610^6,那顶多一道黄题。但是 nn101810^{18},哭泣。

    这里用一个很常见的 trick:矩阵加速优化 DP

    我们设一组矩阵 $\begin{pmatrix} dp_{i, 0} & dp_{i, 1} \end{pmatrix}$。

    然后很容易根据状态转移方程列出关系式:

    $$\begin{pmatrix} dp_{i, 0} & dp_{i, 1} \end{pmatrix} \times \begin{pmatrix} 0 & 1 \\ 1 & 1 \end{pmatrix} = \begin{pmatrix} dp_{i+1, 0} & dp_{i+1, 1} \end{pmatrix} $$

    再根据矩阵跑快速幂就秒掉了这题。

    同上面的,仍然是要初始化两个(具体看代码)。

    时间复杂度 O(logn×t3)O(\log n \times t^3)t=2t = 2(矩阵大小)。

    你会发现这东西很小很小,所以随便过。

    代码

    很轻松拿到 100100 分。跑得飞快,最慢的点 44 毫秒。

    代码轻度整活,但是应该可以看得懂。

    提交记录

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    const int N = 2, mod = 1e9 + 7;
    namespace martix {
    	void mul(int ANS[][N], int x[][N], int y[][N])
    	{
    		int ans[N][N] = {};
    		for (int i = 0; i < N; i++)
    			for (int j = 0; j < N; j++)
    				for (int k = 0; k < N; k++)
    					ans[i][j] = (ans[i][j] + 1ll * x[i][k] * y[k][j]) % mod;
    		memcpy(ANS, ans, sizeof ans);
    	}
    	void ksm(int f[][N], int A[][N], long long y)
    	{
    		while (y)
    		{
    			if (y & 1) mul(f, f, A);
    			mul(A, A, A);
    			y >>= 1;
    		}
    	}
    }; using namespace martix;
    
    void solve()
    {
    	long long n, ans = 0;
    	cin >> n;
    	int zltAK[N][N] = {1, 0}; //第一位填1 的 初始矩阵
    	int ioi[N][N] = {
    		0, 1, 
    		1, 1
    	};
    	ksm(zltAK, ioi, n - 1), ans = (ans + zltAK[0][1]) % mod; //则最后一位只能填2
    
    	int hxyAK[N][N] = {0, 1}; //第一位填2 的 初始矩阵
    	int csp[N][N] = {
    		0, 1, 
    		1, 1
    	};
    	ksm(hxyAK, csp, n - 1), ans = (ans + hxyAK[0][0] + hxyAK[0][1]) % mod; //则最后一位可以填1或2
    
    	cout << ans << '\n';
    }
    int main()
    {
    	int T;
    	scanf("%d", &T);
    	while (T--) solve();
    	return 0;
    }
    

    码字不易,希望能帮助到大家!

    • 1

    信息

    ID
    3842
    时间
    200ms
    内存
    16MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者