#P3332. 第2题-灵动坐标系
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 100
            Accepted: 17
            Difficulty: 8
            
          
          
          
                       所属公司 : 
                              京东
                                
            
                        
              时间 :2025年8月2日
                              
                      
          
 
- 
                        算法标签>动态规划          
 
第2题-灵动坐标系
题目描述
在一个无限大的平面直角坐标系中,小A 从原点出发,每一步只能向“上”、“左”、“右”三个方向之一移动 1 个单位,且不允许经过相同的点。求小A 恰好走到第n 步时的合法走法总数。由于结果可能很大,需对109+7 取模输出。
完整思路
- 
状态定义
- 令an表示恰好走n步的合法路径总数。
 - 令bn表示恰好走n步,且第n步是水平移动(“左”或“右”)的路径数。
 
 - 
分析 “第n步” 的贡献
- 如果第n步向“上”移动,那么前n−1步可以是任意合法路径,共an−1 种情况。
 - 如果第n步水平移动,本来有2个方向,但要排除“回到”第n−1步所在点的情况。若前一步也是水平移动,则会回头;这部分有bn−1种,需要减去。
 
因此 bn=2an−1−bn−1.
 - 
连接an 与 bn
第 n 步总的选择数为“向上”+“水平”,即 an=an−1+bn.
 - 
合并推导
- 由bn定义可得 bn+bn−1=2an−1.
 - 又因为 bn=an−an−1,bn−1=an−1−an−2,
 - 将它们带入上式: (an−an−1)+(an−1−an−2)=2an−1 ⟹an−an−2=2an−1 ⟹an=2an−1+an−2.
 
 - 
初始边界 a0=1,a1=3.
 - 
计算优化
- 将递推写成矩阵形式:

 - 利用2×2 矩阵快速幂,时间复杂度降为O(logn)
 
 - 将递推写成矩阵形式:
 - 
结果取模 全程对109+7 取模,避免溢出。
 
C++
#include <bits/stdc++.h>
using namespace std;
const long long MOD = 1000000007;
// 矩阵乘法
vector<vector<long long>> mul(const vector<vector<long long>>& A,
                              const vector<vector<long long>>& B) {
    vector<vector<long long>> C(2, vector<long long>(2));
    for (int i = 0; i < 2; ++i)
        for (int j = 0; j < 2; ++j)
            for (int k = 0; k < 2; ++k)
                C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % MOD;
    return C;
}
// 矩阵快速幂
vector<vector<long long>> mat_pow(vector<vector<long long>> M, long long p) {
    vector<vector<long long>> R = {{1,0},{0,1}}; // 单位矩阵
    while (p) {
        if (p & 1) R = mul(R, M);
        M = mul(M, M);
        p >>= 1;
    }
    return R;
}
long long count_paths(long long n) {
    if (n == 0) return 1;
    if (n == 1) return 3;
    // 变换矩阵
    vector<vector<long long>> M = {{2,1},{1,0}};
    // 计算 M^(n-1)
    vector<vector<long long>> P = mat_pow(M, n-1);
    // [a_n, a_{n-1}]^T = P * [a_1, a_0]^T = P * [3,1]^T
    return (P[0][0] * 3 + P[0][1] * 1) % MOD;
}
int main() {
    long long n;
    cin >> n;
    cout << count_paths(n) << "\n";
    return 0;
}
Python
MOD = 10**9 + 7
def mat_mul(A, B):
    # 返回 2x2 矩阵 A*B(模 MOD)
    return [
        [(A[0][0]*B[0][0] + A[0][1]*B[1][0]) % MOD, (A[0][0]*B[0][1] + A[0][1]*B[1][1]) % MOD],
        [(A[1][0]*B[0][0] + A[1][1]*B[1][0]) % MOD, (A[1][0]*B[0][1] + A[1][1]*B[1][1]) % MOD]
    ]
def mat_pow(M, p):
    # 矩阵快速幂
    R = [[1, 0], [0, 1]]  # 单位矩阵
    while p:
        if p & 1:
            R = mat_mul(R, M)
        M = mat_mul(M, M)
        p >>= 1
    return R
def count_paths(n):
    if n == 0:
        return 1
    if n == 1:
        return 3
    M = [[2, 1], [1, 0]]
    P = mat_pow(M, n-1)
    # [a_n, a_{n-1}] = P * [a_1, a_0] = P * [3,1]
    return (P[0][0] * 3 + P[0][1] * 1) % MOD
if __name__ == "__main__":
    n = int(input())
    print(count_paths(n))
Java
import java.util.Scanner;
public class Main {
    static final long MOD = 1000000007;
    // 矩阵乘法
    static long[][] mul(long[][] A, long[][] B) {
        long[][] C = new long[2][2];
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 2; j++) {
                for (int k = 0; k < 2; k++) {
                    C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % MOD;
                }
            }
        }
        return C;
    }
    // 矩阵快速幂
    static long[][] matPow(long[][] M, long p) {
        long[][] R = {{1, 0}, {0, 1}}; // 单位矩阵
        while (p > 0) {
            if ((p & 1) == 1) {
                R = mul(R, M);
            }
            M = mul(M, M);
            p >>= 1;
        }
        return R;
    }
    // 计算走 n 步的方案数
    static long countPaths(long n) {
        if (n == 0) return 1;
        if (n == 1) return 3;
        long[][] M = {{2, 1}, {1, 0}};
        long[][] P = matPow(M, n - 1);
        // [a_n, a_{n-1}] = P * [3,1]
        return (P[0][0] * 3 + P[0][1] * 1) % MOD;
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        long n = sc.nextLong();
        System.out.println(countPaths(n));
        sc.close();
    }
}
        题目内容
一觉醒来,小A发现自己身处一个无限大的平面直角坐标系的原点。坐标系之神告诉他,他可以在这个坐标系内移动,但每一步只能选择上,左,右三个方向之一,并朝着这个方向移动1个单位的距离。特别的,坐标系之神要求小A不能经过相同的点。
坐标系之神告诉小A,只要他能算出走n步的合法方案数,就把他放回现实世界。小A刚醒来还无法深入思考,想你帮他计算一下结果。
输入描述
输入只有一行,包含1个整数n(1<=n<=1000000000)
输出描述
一行输出1个整数,代表题目所求答案,如果答案过大,请将其对109+7取模。
注:109+7即1000000007,取模即求余运算。
样例1
输入
2
输出
7
说明
从(0,0)出发走2步,共7种合法走法:
(0,0)−>(0,1)−>(0,2)
(0,0)−>(0,1)−>(1,1)
(0,0)−>(0,1)−>(−1,1)
(0,0)−>(1,0)−>(2,0)
(0,0)−>(1,0)−>(1,1)
(0,0)−>(−1,0)−>(−2,0)
(0,0)−>(−1,0)−>(−1,1)