当前位置: 首页 > news >正文

【2025 深圳大学-腾讯云程序设计竞赛(热身赛)】题解

比赛链接

A. Cloud Studio的共享连接

题目大意 && Solution

给定 T T T 组长度均为 12 12 12 的字符串 s s s

对每个 s s s,将其按从左到右的顺序两两分组形成 6 6 6 A S C I I \rm{ASCII} ASCII 码,对这 6 6 6 A S C I I \rm{ASCII} ASCII 码都 + 50 +50 +50,作为输出的字符串(即长度为 6 6 6 的新字符串)。

直接模拟即可。

时间复杂度 O ( 12 ⋅ T ) O(12 \cdot T) O(12T)

C++ Code

#include <bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned;
using i80 = __int128_t;
using u80 = unsigned __int128_t;
using f64 = double;
using f80 = long double;
using i16 = short;
using u16 = unsigned short;

constexpr f64 pi = std::numbers::pi;

std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());

void solve() {
    std::string s;
    std::cin >> s;

    std::string ans;
    for (int i = 0; i + 2 <= s.size(); i += 2) {
        ans += char(std::stoi(s.substr(i, 2)) + 50);
    }
    std::cout << ans << "\n";
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    std::cout << std::fixed << std::setprecision(12);
    
    int T = 1;
    std::cin >> T;
    
    while (T--) {
        solve();
    }
    
    return 0;
}

B. 都是绘绘,都是早凉的老婆

题目大意

给定 T T T 组数据,每组数据给定一棵树。

对于每棵 n n n 个节点的树, A l i c e \rm{Alice} Alice B o b \rm{Bob} Bob 想玩博弈论,但是不想玩得太简单,于是让 B o b \rm{Bob} Bob 一开始选择一个点集 V V V,两人就在 V V V生成子图 G = ( V , E ) G = (V, E) G=(V,E) 上开始玩, A l i c e \rm{Alice} Alice 先手。

当前玩家可以选择删掉 G G G 中的一条边,也可以删掉一个点及其所有相连的边。不能操作的玩家将输掉比赛;特别的,若 G G G 为空,视为 B o b \rm{Bob} Bob 胜利。

当然这都不重要,因为这是 2023 C C P C \rm{2023CCPC} 2023CCPC 哈尔滨的题目,结论是:

奇数个点的树的 S G \rm{SG} SG 值为 1 1 1,偶数个点的树 S G \rm{SG} SG 值为 2 2 2 B o b \rm{Bob} Bob 选择的点集 V V V 只需要满足每个连通块的 S G \rm{SG} SG 值异或起来等于 0 0 0 就有必胜策略。

特别的,空集的 S G \rm{SG} SG 值为 0 0 0

在此基础上,本题想问: B o b \rm{Bob} Bob 有多少种方案选择点集 V V V 能够获胜。

两种方案不同,当且仅当两次选择的点集 V V V 不同。答案对 998244353 998244353 998244353 取模。

数据范围

  • 1 ≤ ∑ n ≤ 1 0 6 1 \leq \sum n \leq 10^6 1n106

Solution

我们把异或起来为零翻译一下,可以得到:

  • ∣ V ∣ |V| V 一定为偶数,因为最终异或值要为 0 0 0,即不论出现多少个 1 1 1 还是 2 2 2,都要求出现偶数个奇数点连通块或偶数个偶数点连通块;
  • ∣ E ∣ |E| E 一定为偶数,因为对每个连通块来说都有 ∣ E i ∣ = ∣ V i ∣ − 1 |E_i| = |V_i| - 1 Ei=Vi1(树中拆出来的连通块的性质)。

定义 d p [ x ] [ 0 / 1 ] [ 0 / 1 ] [ 0 / 1 ] \rm{dp}[x][0/1][0/1][0/1] dp[x][0/1][0/1][0/1] 为总方案数,细节如下。

  • 对当前结点 x x x
  • 第一个 0 / 1 0/1 0/1 表示 不选 / 选 \rm{不选}/\rm{选} 不选/ x x x 加入 V V V
  • 第二个 0 / 1 0/1 0/1 表示已经选择的点集 V V V 大小的奇偶, ∣ V ∣ |V| V 为偶(奇)时为 0 0 0 1 1 1),
  • 第三个 0 / 1 0/1 0/1 表示由已选择点集 V V V 导出的边集 E E E 大小的奇偶, ∣ E ∣ |E| E 为偶(奇)时为 0 0 0 1 1 1)。

下面我们定义 二维矩阵的三个运算 ⊕ \oplus 为异或运算)。

  • 加法 + + +:与正常的矩阵一样,对于 c = a + b c = a + b c=a+b,有 c [ i ] [ j ] = a [ i ] [ j ] + b [ i ] [ j ] ,   0 ≤ i , j < 2. c[i][j] = a[i][j] + b[i][j], \ 0 \leq i, j < 2. c[i][j]=a[i][j]+b[i][j], 0i,j<2.
  • 乘法 × \times ×:乘法被重新定义,对于 c = a × b c = a \times b c=a×b,有 c [ i ⊕ u ] [ j ⊕ v ] = c [ i ⊕ u ] [ j ⊕ v ] + a [ i ] [ j ] × b [ u ] [ v ] ,   0 ≤ i , j , u , v < 2. c[i \oplus u][j \oplus v] = c[i \oplus u][j \oplus v] + a[i][j] \times b[u][v], \ 0 \leq i, j, u, v < 2. c[iu][jv]=c[iu][jv]+a[i][j]×b[u][v], 0i,j,u,v<2. 这里的乘法意思是: i i i u u u 分别表示点集大小 ∣ V ∣ |V| V 的奇偶, j j j v v v 分别表示边集大小 ∣ E ∣ |E| E 的奇偶,这样合并二者的方案数只需要枚举 2 4 2^4 24 种情况;而最终新的奇偶性就是 i ⊕ u i \oplus u iu j ⊕ v j \oplus v jv
  • 取反 ! ! !:也是重新定义,对于 c =   ! a c = \ !a c= !a,有 c [ i ] [ j ⊕ 1 ] = a [ i ] [ j ] ,   0 ≤ i , j < 2. c[i][j \oplus 1] = a[i][j], \ 0 \leq i, j < 2. c[i][j1]=a[i][j], 0i,j<2. 这里的取反意思是: i i i 表示点集 ∣ V ∣ |V| V 大小的奇偶, j j j 表示边集大小 ∣ E ∣ |E| E 的奇偶,当我确定我加入了 1 1 1 条新的边,边集的奇偶性就发生改变,所以最后一个维度的方案数应该调换。

下面考虑状态转移。

  • d p [ x ] [ 0 ] = d p [ x ] [ 0 ] × ( d p [ y ] [ 0 ] + d p [ y ] [ 1 ] ) \rm{dp[x][0] = dp[x][0] \times (dp[y][0] + dp[y][1])} dp[x][0]=dp[x][0]×(dp[y][0]+dp[y][1]),这表示对于 x x x 及其子树,不选择 x x x,那么选择 y y y 不会 引起边的奇偶性变化,只要将其合并到 d p [ x ] [ 0 ] \rm{dp[x][0]} dp[x][0] 中即可;
  • d p [ x ] [ 1 ] = d p [ x ] [ 1 ] × ( d p [ y ] [ 0 ]   +   ! d p [ y ] [ 1 ] ) \rm{dp[x][1] = dp[x][1] \times (dp[y][0] \ + \ !dp[y][1])} dp[x][1]=dp[x][1]×(dp[y][0] + !dp[y][1]),这表示对于 x x x 及其子树,选择了 x x x,那么选择 y y y 引起边的奇偶性变化,需要取反并将其合并到 d p [ x ] [ 1 ] \rm{dp[x][1]} dp[x][1]

初始化:对于 x ∈ [ 1 , n ] x \in [1, n] x[1,n],均有 d p [ x ] [ 0 ] [ 0 ] [ 0 ] = d p [ x ] [ 1 ] [ 1 ] [ 0 ] = 1 \rm{dp[x][0][0][0] = dp[x][1][1][0] = 1} dp[x][0][0][0]=dp[x][1][1][0]=1,分别表示 0 0 0 个点 0 0 0 条边,以及 1 1 1 个点 0 0 0 条边,都是一种方案。

最终答案 d p [ 0 ] [ 0 ] [ 0 ] [ 0 ] + d p [ 0 ] [ 1 ] [ 0 ] [ 0 ] \rm{dp[0][0][0][0] + dp[0][1][0][0]} dp[0][0][0][0]+dp[0][1][0][0]

时间复杂度 O ( 2 4 ∑ n ) O(2^4\sum \rm{n}) O(24n)

  • 常数会稍大, 1 0 6 10^6 106 的递归栈消耗很大。

C++ Code

#include <bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned;
using i80 = __int128_t;
using u80 = unsigned __int128_t;
using f64 = double;
using f80 = long double;
using i16 = short;
using u16 = unsigned short;

constexpr f64 pi = std::numbers::pi;

std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());

template<class T>
constexpr T power(T a, i64 b) {
    T res = 1;
    for (; b; b /= 2, a *= a) {
        if (b % 2) {
            res *= a;
        }
    }
    return res;
}
template<int P>
struct MInt {
    int x;
    constexpr MInt() : x{} {}
    constexpr MInt(i64 x) : x{norm(x % getMod())} {}
     
    static int Mod;
    constexpr static int getMod() {
        if (P > 0) {
            return P;
        } else {
            return Mod;
        }
    }
    constexpr static void setMod(int Mod_) {
        Mod = Mod_;
    }
    constexpr int norm(int x) const {
        if (x < 0) {
            x += getMod();
        }
        if (x >= getMod()) {
            x -= getMod();
        }
        return x;
    }
    constexpr int val() const {
        return x;
    }
    explicit constexpr operator int() const {
        return x;
    }
    constexpr MInt operator-() const {
        MInt res;
        res.x = norm(getMod() - x);
        return res;
    }
    constexpr MInt inv() const {
        assert(x != 0);
        return power(*this, getMod() - 2);
    }
    constexpr MInt &operator*=(MInt rhs) & {
        x = 1LL * x * rhs.x % getMod();
        return *this;
    }
    constexpr MInt &operator+=(MInt rhs) & {
        x = norm(x + rhs.x);
        return *this;
    }
    constexpr MInt &operator-=(MInt rhs) & {
        x = norm(x - rhs.x);
        return *this;
    }
    constexpr MInt &operator/=(MInt rhs) & {
        return *this *= rhs.inv();
    }
    friend constexpr MInt operator*(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res *= rhs;
        return res;
    }
    friend constexpr MInt operator+(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res += rhs;
        return res;
    }
    friend constexpr MInt operator-(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res -= rhs;
        return res;
    }
    friend constexpr MInt operator/(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res /= rhs;
        return res;
    }
    friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {
        i64 v;
        is >> v;
        a = MInt(v);
        return is;
    }
    friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) {
        return os << a.val();
    }
    friend constexpr bool operator==(MInt lhs, MInt rhs) {
        return lhs.val() == rhs.val();
    }
    friend constexpr bool operator!=(MInt lhs, MInt rhs) {
        return lhs.val() != rhs.val();
    }
};
 
template<>
int MInt<0>::Mod = 998244353;
 
template<int V, int P>
constexpr MInt<P> CInv = MInt<P>(V).inv();
 
constexpr int P = 998244353;
using Z = MInt<P>;

using Vector = std::array<Z, 2>;
using Matrix = std::array<Vector, 2>;
using Tensor = std::array<Matrix, 2>;

Matrix operator+(const Matrix &a, const Matrix &b) {
    Matrix c{};
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2; j++) {
            c[i][j] = a[i][j] + b[i][j];
        }
    }
    return std::move(c);
}
Matrix operator*(const Matrix &a, const Matrix &b) {
    Matrix c{};
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2; j++) {
            if (a[i][j] == 0) {
                continue;
            }
            for (int u = 0; u < 2; u++) {
                for (int v = 0; v < 2; v++) {
                    c[i ^ u][j ^ v] += a[i][j] * b[u][v];
                }
            }
        }
    }
    return std::move(c);
}
Matrix operator!(const Matrix &a) {
    auto c = a;
    for (int i = 0; i < 2; i++) {
        std::swap(c[i][0], c[i][1]);
    }
    return std::move(c);
}

void solve() {
    int n;
    std::cin >> n;

    std::vector<std::vector<int>> adj(n);
    for (int i = 1; i < n; i++) {
        int u, v;
        std::cin >> u >> v;
        u--, v--;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }

    std::vector<Tensor> dp(n);
    auto dfs = [&](auto &&self, int x) -> void {
        dp[x][0][0][0] = 1;
        dp[x][1][1][0] = 1;
        for (int y: adj[x]) {
            adj[y].erase(std::ranges::find(adj[y], x));
            self(self, y);
            auto res = dp[y][0] + dp[y][1];
            dp[x][0] = dp[x][0] * res;
            res = dp[y][0] + !dp[y][1];
            dp[x][1] = dp[x][1] * res;
        }
    };
    dfs(dfs, 0);

    auto ans = dp[0][0] + dp[0][1];
    std::cout << ans[0][0] << "\n";
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    std::cout << std::fixed << std::setprecision(12);
    
    int T = 1;
    std::cin >> T;
    
    while (T--) {
        solve();
    }
    
    return 0;
}

C. Anon也想玩双影奇境

题目大意

给定 T T T 组数据,每组有两个正整数 n , m n, m n,m

对每个 n , m n, m n,m,要求算出其二进制表示有多少位是相同的(如果长度不一样就补前导零)。

例如 n = 2 , m = 19 n = 2, m = 19 n=2,m=19,其二进制表示分别是 00010 , 10011 00010, 10011 00010,10011,有 3 3 3 位相同。

Solution

用短除法将 n , m n, m n,m 转化为二进制字符串,并补齐前导零后,直接遍历比较即可。

时间复杂度 O ( ∑ log ⁡ max ⁡ ( n , m ) ) O(\sum\log\max(\rm{n}, \rm{m})) O(logmax(n,m))

C++ Code

#include <bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned;
using i80 = __int128_t;
using u80 = unsigned __int128_t;
using f64 = double;
using f80 = long double;
using i16 = short;
using u16 = unsigned short;

constexpr f64 pi = std::numbers::pi;

std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());

void solve() {
    int n, m;
    std::cin >> n >> m;

    auto get = [&](int x) {
        std::string ans;
        for (int y = x; y > 0; y /= 2) {
            ans += char(y % 2 + '0');
        }
        return ans;
    };
    auto sn = get(n);
    auto sm = get(m);
    while (sn.size() < sm.size()) {
        sn += '0';
    }
    while (sm.size() < sn.size()) {
        sm += '0';
    }
    int N = sn.size();
    std::cout << std::ranges::count_if(std::views::iota(0, N), [&](int i) {
        return sn[i] == sm[i];
    }) << "\n";
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    std::cout << std::fixed << std::setprecision(12);
    
    int T = 1;
    std::cin >> T;
    
    while (T--) {
        solve();
    }
    
    return 0;
}

D. 诗寇蒂Skuld

题目大意

给定 n n n 朵花的信息 a i , l i , r i a_i, l_i, r_i ai,li,ri,分别表示第 i i i 朵花的 美丽值 以及 开花的区间 [ l i , r i ] [l_i, r_i] [li,ri]。再给定一个 m m m 表示你可以施展魔法的次数;每次施展魔法你可以做如下操作一次:

  • 选定一个下标 i i i,要么令 l i : = l i − 1 l_i := l_i - 1 li:=li1,要么令 r i : = r i + 1 r_i := r_i + 1 ri:=ri+1

现在规定某一天的美丽值 A A A 为** 所有盛开的花的美丽值之和**,求出最多使用 m m m 次魔法后的 max ⁡ ( A ) \max(A) max(A)

数据范围

  • 1 ≤ n ≤ 100 1 \leq n \leq 100 1n100
  • 0 ≤ m ≤ 2025 0 \leq m \leq 2025 0m2025
  • 1 ≤ a i , l i , r i ≤ 1 0 9 1 \leq a_i, l_i, r_i \leq 10^9 1ai,li,ri109

Solution

首先给出结论,这种题目的 某一天 一定可以在某个端点 l i l_i li r i r_i ri 处取到。

假设我们是在 非端点处 d d d 求得了 max ⁡ ( A ) \max(A) max(A),那么一定存在一些 i i i 使得 d ∈ ( l i , r i ) d \in (l_i, r_i) d(li,ri),否则去到区间并 ⋃ i = 1 n [ l i , r i ] \bigcup\limits_{i = 1}^{n} [l_i, r_i] i=1n[li,ri] 都没有覆盖的地方,一定不会最优。此时对于那些不包含 d d d 的区间 [ l j , r j ] [l_j, r_j] [lj,rj],一定有 d < l j d < l_j d<lj d > r j d > r_j d>rj(因为 d d d 一定不在端点)。

设对于当前 d d d

  • 包含 d d d 的区间下标集合为 C C C
  • 左边有 c l c_l cl 个区间可以通过魔法改变右端点到达 d d d,操作 s l s_l sl 次;
  • 右边有 c r c_r cr 个区间可以通过魔法改变左端点到达 d d d,操作 s r s_r sr 次。

下面说明我们可以将 d d d 移到端点而保持答案不变甚至更优。

  • c l < c r c_l < c_r cl<cr,那我们可以找到一个 i ∈ C i \in C iC,使得 r i = min ⁡ k ∈ C r k r_i = \min\limits_{k \in C}r_k ri=kCminrk,此时将 d d d 右移到 r i r_i ri,那么 s l : = s l + c l , s r : = s r − c r , s_l := s_l + c_l, \\ s_r := s_r - c_r, sl:=sl+cl,sr:=srcr, 这样一来总的操作次数 s = s l + s r s = s_l + s_r s=sl+sr 就变小了,而且美丽值不变;
  • c l > c r c_l > c_r cl>cr,那我们可以找到一个 i ∈ C i \in C iC,使得 l i = max ⁡ k ∈ C l k l_i = \max\limits_{k \in C}l_k li=kCmaxlk,此时将 d d d 左移到 l i l_i li,那么 s l : = s l − c l , s r : = s r + c r , s_l := s_l - c_l, \\ s_r := s_r + c_r, sl:=slcl,sr:=sr+cr, 这样一来总的操作次数 s = s l + s r s = s_l + s_r s=sl+sr 就变小了,而且美丽值不变;
  • c l = c r c_l = c_r cl=cr,那我们随便往上述的 l i l_i li r i r_i ri 移,总操作次数 s s s 不变。

综上,在端点处一定可以取到 max ⁡ ( A ) \max(A) max(A)

下设 c o s t i ( d ) cost_i(d) costi(d) 函数( i ∈ [ 1 , n ] i \in [1, n] i[1,n]):

c o s t i ( d ) = { l i − d , d < l i , 0 , d ∈ [ l i , r i ] d − r i , d > r i . cost_i(d) = \begin{cases} l_i - d,& d < l_i, \\ 0,& d \in [l_i, r_i] \\ d - r_i,& d > r_i. \end{cases} costi(d)= lid,0,dri,d<li,d[li,ri]d>ri.

对于当前的 d d d,可以把 c o s t i ( d ) cost_i(d) costi(d) 看成物品体积,把 a i a_i ai 看成物品价值,这样就变成了背包容量为 m m m 的一个 01 01 01 背包问题了。

时间复杂度 O ( n 2 m ) O(\rm{n}^2\rm{m}) O(n2m)

C++ Code

#include <bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned;
using i80 = __int128_t;
using u80 = unsigned __int128_t;
using f64 = double;
using f80 = long double;
using i16 = short;
using u16 = unsigned short;

constexpr f64 pi = std::numbers::pi;

template<class T, size_t L>
std::istream &operator>>(std::istream &is, std::array<T, L> &a) {
    for (auto &x: a) {
        is >> x;
    }
    return is;
}
template<class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
    for (auto &x: v) {
        is >> x;
    }
    return is;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n, m;
    std::cin >> n >> m;

    std::vector<std::array<int, 3>> flower(n);
    std::cin >> flower;

    std::vector<int> days;
    for (const auto &[a, l, r]: flower) {
        days.push_back(l);
        days.push_back(r);
    }
    std::ranges::sort(days);
    days.erase(std::unique(days.begin(), days.end()), days.end());

    i64 ans = 0;
    for (int d: days) {
        i64 sum = 0;
        std::vector<std::pair<int, int>> obj;
        for (const auto &[a, l, r]: flower) {
            if (d < l) {
                obj.emplace_back(l - d, a);
            } else if (d > r) {
                obj.emplace_back(d - r, a);
            } else {
                sum += a;
            }
        }
        std::vector<i64> dp(m + 1);
        for (const auto &[v, w]: obj) {
            for (int s = m; s >= v; s--) {
                dp[s] = std::max(dp[s], dp[s - v] + w);
            }
        }
        ans = std::max(ans, sum + dp[m]);
    }
    std::cout << ans << "\n";
    
    return 0;
}

E. 乌尔德+Urd

题目大意

给定 T T T 组数据,每组给定三个正整数 n , m , p n, m, p n,m,p

设有无穷多项的斐波那契数列 f 1 = 1 , f 2 = 1 , f 3 = 2 , ⋯ f_1 = 1, f_2 = 1, f_3 = 2, \cdots f1=1,f2=1,f3=2,

对每个 n , m , p n, m, p n,m,p,求 ∑ i = 1 n [ m ∣ i ] ⋅ f i . \sum\limits_{i = 1}^{n}[m \mid i] \cdot f_i. i=1n[mi]fi.

其中 m ∣ i m \mid i mi 表示 m m m 可以整除 i i i,例如 2 ∣ 4 2 \mid 4 24 f i f_i fi 表示斐波那契数列的第 i i i 项。

数据范围

  • 1 ≤ T ≤ 1 0 5 1 \leq T \leq 10^5 1T105
  • 1 ≤ n , m ≤ 1 0 18 1 \leq n, m \leq 10^{18} 1n,m1018
  • 1 ≤ p ≤ 1 0 9 1 \leq p \leq 10^9 1p109

Solution

其实在斐波那契这里看见这么大的数据范围,就应该立即想到矩阵快速幂了。

我们特别规定 f 0 = 0 f_0 = 0 f0=0。考虑一个二维向量 F n = [ f n , f n + 1 ] F_n = [f_n, f_{n + 1}] Fn=[fn,fn+1],以及一个转移矩阵

A = [ 0 1 1 1 ] , A = \begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix}, A=[0111],

这样一来就有 F n + 1 = F n × A F_{n + 1} = F_n \times A Fn+1=Fn×A,根据矩阵的结合律,有 F n = F 0 × A n F_n = F_0 \times A^n Fn=F0×An

那么求和式可以化简为( F n [ 0 ] = f n F_n[0] = f _n Fn[0]=fn

∑ i = 1 n [ m ∣ i ] ⋅ f i = ∑ i = 1 ⌊ n m ⌋ f i ⋅ m = ∑ i = 1 ⌊ n m ⌋ F i ⋅ m [ 0 ] = ∑ i = 1 ⌊ n m ⌋ ( F 0 × A i ⋅ m ) [ 0 ] = ( F 0 ∑ i = 1 ⌊ n m ⌋ ( A m ) i ) [ 0 ] = ( [ 0 , 1 ] ∑ i = 1 ⌊ n m ⌋ ( A m ) i ) [ 0 ] = ∑ i = 1 ⌊ n m ⌋ ( B i ) [ 1 ] [ 0 ] . \begin{align*} \sum\limits_{i = 1}^{n}[m \mid i] \cdot f_i &= \sum\limits_{i = 1}^{\lfloor \frac{n}{m} \rfloor}f_{i \cdot m} \\ &= \sum\limits_{i = 1}^{\lfloor \frac{n}{m} \rfloor}F_{i \cdot m}[0] \\ &= \sum\limits_{i = 1}^{\lfloor \frac{n}{m} \rfloor}(F_0 \times A^{i \cdot m})[0] \\ &= (F_0\sum\limits_{i = 1}^{\lfloor \frac{n}{m} \rfloor}(A^m)^i)[0] \\ &= ([0, 1]\sum\limits_{i = 1}^{\lfloor \frac{n}{m} \rfloor}(A^m)^i)[0] \\ &= \sum\limits_{i = 1}^{\lfloor \frac{n}{m} \rfloor} (B^i)[1][0]. \end{align*} i=1n[mi]fi=i=1mnfim=i=1mnFim[0]=i=1mn(F0×Aim)[0]=(F0i=1mn(Am)i)[0]=([0,1]i=1mn(Am)i)[0]=i=1mn(Bi)[1][0].

其中 B = A m B = A^m B=Am B [ 1 ] [ 0 ] B[1][0] B[1][0] 表示 B B B 这个二维矩阵第 1 1 1 行第 0 0 0 列的元素(下标从 0 0 0 开始)。

那么我们其实可以求出 ∑ i = 1 ⌊ n m ⌋ B i \sum\limits_{i = 1}^{\lfloor \frac{n}{m} \rfloor}B^i i=1mnBi 后,再取其第 1 1 1 行第 0 0 0 列的元素。而考虑单位阵 E = [ 1 0 0 1 ] E = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix} E=[1001] 的左下角是 0 0 0,所以我们可以求 E + ∑ i = 1 ⌊ n m ⌋ B i = ∑ i = 0 ⌊ n m ⌋ B i E + \sum\limits_{i = 1}^{\lfloor \frac{n}{m} \rfloor}B^i = \sum\limits_{i = 0}^{\lfloor \frac{n}{m} \rfloor}B^i E+i=1mnBi=i=0mnBi

下设 N = ⌊ n m ⌋ N = \lfloor \frac{n}{m} \rfloor N=mn,我们考虑分治求解 ∑ i = 0 N B i \sum\limits_{i = 0}^{N}B^i i=0NBi

  • N = 0 N = 0 N=0,直接返回 E E E
  • N N N 为奇数,那么式子可以拆成长度相同的两部分,分别是 E + A + A 2 + ⋯ + A ⌊ N 2 ⌋ , A ⌊ N 2 ⌋ + 1 + A ⌊ N 2 ⌋ + 2 + ⋯ + A N . E + A + A^2 + \cdots + A^{\lfloor \frac{N}{2} \rfloor}, \\ A^{\lfloor \frac{N}{2} \rfloor + 1} + A^{\lfloor \frac{N}{2} \rfloor +2} + \cdots + A^N. E+A+A2++A2N,A2N+1+A2N+2++AN. 而把下面提一个 A ⌊ N 2 ⌋ + 1 A^{\lfloor \frac{N}{2} \rfloor + 1} A2N+1 就可以得到上面,所以直接算前一半,最后乘以 ( E + A ⌊ N 2 ⌋ + 1 ) (E + A^{\lfloor \frac{N}{2} \rfloor + 1}) (E+A2N+1) 即可。
  • N N N 为偶数,先把 E E E 去掉,再对后面的式子提一个 A A A,就可以得到 E + A ( E + A + A 2 + ⋯ + A N − 1 ) , E + A(E + A + A^2 + \cdots + A^{N - 1}), E+A(E+A+A2++AN1), 后面的 ( E + A + A 2 + ⋯ + A N − 1 ) (E + A + A^2 + \cdots + A^{N - 1}) (E+A+A2++AN1) 就是 N − 1 N - 1 N1 为奇数的情况。

时间复杂度 O ( ∑ ( log ⁡ m + log ⁡ ⌊ n m ⌋ ) ) = O ( ∑ log ⁡ n ) O(\sum(\log m + \log \lfloor \frac{n}{m} \rfloor)) = O(\sum\log n) O((logm+logmn⌋))=O(logn)

  • 其中 2 × 2 2 \times 2 2×2 矩阵的常数基本可以忽略。

C++ Code

#include <bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned;
using i80 = __int128_t;
using u80 = unsigned __int128_t;
using f64 = double;
using f80 = long double;
using i16 = short;
using u16 = unsigned short;

constexpr f64 pi = std::numbers::pi;

std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());

template<int P>
struct MInt {
    int x;
    constexpr MInt() : x{} {}
    constexpr MInt(i64 x) : x{norm(x % getMod())} {}
     
    static int Mod;
    constexpr static int getMod() {
        if (P > 0) {
            return P;
        } else {
            return Mod;
        }
    }
    constexpr static void setMod(int Mod_) {
        Mod = Mod_;
    }
    constexpr int norm(int x) const {
        if (x < 0) {
            x += getMod();
        }
        if (x >= getMod()) {
            x -= getMod();
        }
        return x;
    }
    constexpr int val() const {
        return x;
    }
    explicit constexpr operator int() const {
        return x;
    }
    constexpr MInt operator-() const {
        MInt res;
        res.x = norm(getMod() - x);
        return res;
    }
    constexpr MInt inv() const {
        assert(x != 0);
        return power(*this, getMod() - 2);
    }
    constexpr MInt &operator*=(MInt rhs) & {
        x = 1LL * x * rhs.x % getMod();
        return *this;
    }
    constexpr MInt &operator+=(MInt rhs) & {
        x = norm(x + rhs.x);
        return *this;
    }
    constexpr MInt &operator-=(MInt rhs) & {
        x = norm(x - rhs.x);
        return *this;
    }
    constexpr MInt &operator/=(MInt rhs) & {
        return *this *= rhs.inv();
    }
    friend constexpr MInt operator*(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res *= rhs;
        return res;
    }
    friend constexpr MInt operator+(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res += rhs;
        return res;
    }
    friend constexpr MInt operator-(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res -= rhs;
        return res;
    }
    friend constexpr MInt operator/(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res /= rhs;
        return res;
    }
    friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {
        i64 v;
        is >> v;
        a = MInt(v);
        return is;
    }
    friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) {
        return os << a.val();
    }
    friend constexpr bool operator==(MInt lhs, MInt rhs) {
        return lhs.val() == rhs.val();
    }
    friend constexpr bool operator!=(MInt lhs, MInt rhs) {
        return lhs.val() != rhs.val();
    }
};
 
template<>
int MInt<0>::Mod = 998244353;
 
template<int V, int P>
constexpr MInt<P> CInv = MInt<P>(V).inv();
 
constexpr int P = 0;
using Z = MInt<P>;

template<class T>
T power(T a, i64 b) {
    T res;
    res.unit(a.n);
    for ( ; b; b /= 2, a *= a) {
        if (b & 1) {
            res *= a;
        }
    }
    return res;
}
template<class T>
struct Matrix {
    int n, m;
    std::vector<std::vector<T>> g;
    Matrix() {}
    Matrix(int _n, int _m) {
        init(_n, _m);
    }
    Matrix(int _n) {
        init(_n);
    }
    template<class T2>
    Matrix(std::vector<std::vector<T2>> _g) {
        init(_g);
    }
    void init(int _n, int _m) {
        n = _n, m = _m;
        g.resize(n);
        for (int i = 0; i < n; i += 1) {
            g[i].resize(m);
        }
    }
    void init(int _n) {
        init(_n, _n);
    }
    void unit(int _n) {
        init(_n, _n);
        for (int i = 0; i < n; i += 1) {
            g[i][i] = 1;
        }
    }
    template<class T2>
    void init(std::vector<std::vector<T2>> _g) {
        init(_g.size(), _g[0].size());
        for (int i = 0; i < n; i += 1) {
            for (int j = 0; j < m; j += 1) {
                g[i][j] = T(_g[i][j]);
            }
        }
    }
    std::vector<T> &operator[](int i) { 
        return g[i];
    }
    void fill(const T v) {
        for (int i = 0; i < n; i += 1) {
            g[i].assign(m, v);
        }
    }
    // 0:逆时针,1:顺时针
    void rotate(int d, int x1, int y1, int x2, int y2) {
        assert(d == 0 or d == 1);
        Matrix<T> G(n, m);
        if (x2 - x1 == n and y2 - y1 == m) {
            G.init(m, n);
        }
        if (d == 0) {
            for (int i = 0; i < n; i += 1) {
                for (int j = 0; j < m; j += 1) {
                    if (i >= x1 and i < x2 and j >= y1 and j < y2) {
                        G[x2 - (j - y1) - 1][y1 + (i - x1)] = g[i][j];
                    } else {
                        G[i][j] = g[i][j];
                    }
                }
            }
        } else {
            for (int i = 0; i < n; i += 1) {
                for (int j = 0; j < m; j += 1) {
                    if (i >= x1 and i < x2 and j >= y1 and j < y2) {
                        G[x1 + (j - y1)][y2 - (i - x1) - 1] = g[i][j];
                    } else {
                        G[i][j] = g[i][j];
                    }
                }
            }
        }
        *this = G;
    }
    void rotate(int d) {
        rotate(d, 0, 0, n, m);
    }
    // 0:上下翻转,1:左右翻转
    void flip(int d, int x1, int y1, int x2, int y2) {
        assert(d == 0 or d == 1);
        Matrix<T> G(n, m);
        if (d == 0) {
            for (int i = 0; i < n; i += 1) {
                for (int j = 0; j < m; j += 1) {
                    if (i >= x1 and i < x2 and j >= y1 and j < y2) {
                        G[(x1 + x2) - i - 1][j] = g[i][j];
                    } else {
                        G[i][j] = g[i][j];
                    }
                }
            }
        } else {
            for (int i = 0; i < n; i += 1) {
                for (int j = 0; j < m; j += 1) {
                    if (i >= x1 and i < x2 and j >= y1 and j < y2) {
                        G[i][(y1 + y2) - j - 1] = g[i][j];
                    } else {
                        G[i][j] = g[i][j];
                    }
                }
            }
        }
        *this = G;
    }
    void flip(int d) {
        flip(d, 0, 0, n, m);
    }
    Matrix<T> transpose() {
        Matrix<T> res(m, n);
        for (int i = 0; i < n; i += 1) {
            for (int j = 0; j < m; j += 1) {
                res[j][i] = g[i][j];
            }
        }
        return res;
    }
    template<class T2>
    Matrix<T> &operator+=(Matrix<T2> A) & {
        assert(n == A.n and m == A.m);
        for (int i = 0; i < n; i += 1) {
            for (int j = 0; j < m; j += 1) {
                g[i][j] += T(A[i][j]);
            }
        }
        return *this;
    }
    template<class T2>
    friend Matrix<T> operator+(Matrix<T> A, Matrix<T2> B) {
        Matrix<T> res = A;
        res += B;
        return res;
    }
    template<class T2>
    Matrix<T> &operator-=(Matrix<T2> A) & {
        assert(n == A.n and m == A.m);
        for (int i = 0; i < n; i += 1) {
            for (int j = 0; j < m; j += 1) {
                g[i][j] -= T(A[i][j]);
            }
        }
        return *this;
    }
    template<class T2>
    friend Matrix<T> operator-(Matrix<T> A, Matrix<T2> B) {
        Matrix<T> res = A;
        res -= B;
        return res;
    }
    template<class T2>
    Matrix<T> &operator*=(Matrix<T2> A) & {
        assert(m == A.n);
        Matrix<T> res(n, A.m);
        for (int i = 0; i < n; i += 1) {
            for (int j = 0; j < A.m; j += 1) {
                for (int k = 0; k < m; k += 1) {
                    res[i][j] += g[i][k] * T(A[k][j]);
                }
            }
        }
        return *this = res;
    }
    template<class T2>
    friend Matrix<T> operator*(Matrix<T> A, Matrix<T2> B) {
        Matrix<T> res = A;
        res *= B;
        return res;
    }
    friend std::istream &operator>>(std::istream &is, Matrix<T> &A) {
        for (int i = 0; i < A.n; i += 1) {
            for (int j = 0; j < A.m; j += 1) {
                is >> A[i][j];
            }
        }
        return is;
    }
    friend std::ostream &operator<<(std::ostream &os, Matrix<T> &A) {
        for (int i = 0; i < A.n; i += 1) {
            for (int j = 0; j < A.m; j += 1) {
                os << A[i][j] << " \n"[j + 1 == A.m];
            }
        }
        return os;
    }
    friend bool operator==(Matrix<T> A, Matrix<T> B) {
        if (A.n != B.n) {
            return false;
        } else if (A.m != B.m) {
            return false;
        }
        for (int i = 0; i < A.n; i += 1) {
            for (int j = 0; j < A.m; j += 1) {
                if (A[i][j] != B[i][j]) {
                    return false;
                }
            }
        }
        return true;
    }
    friend bool operator!=(Matrix<T> A, Matrix<T> B) {
        if (A.n != B.n) {
            return true;
        } else if (A.m != B.m) {
            return true;
        }
        for (int i = 0; i < A.n; i += 1) {
            for (int j = 0; j < A.m; j += 1) {
                if (A[i][j] != B[i][j]) {
                    return true;
                }
            }
        }
        return false;
    }
    friend bool operator<(Matrix<T> A, Matrix<T> B) {
        if (A.n != B.n) {
            return A.n < B.n;
        } else if (A.m != B.m) {
            return A.m < B.m;
        }
        for (int i = 0; i < A.n; i += 1) {
            for (int j = 0; j < A.m; j += 1) {
                if (A[i][j] != B[i][j]) {
                    return A[i][j] < B[i][j];
                }
            }
        }
        return false;
    }
    friend bool operator>(Matrix<T> A, Matrix<T> B) {
        if (A.n != B.n) {
            return A.n > B.n;
        } else if (A.m != B.m) {
            return A.m > B.m;
        }
        for (int i = 0; i < A.n; i += 1) {
            for (int j = 0; j < A.m; j += 1) {
                if (A[i][j] != B[i][j]) {
                    return A[i][j] > B[i][j];
                }
            }
        }
        return false;
    }
};

void solve() {
    i64 n, m, p;
    std::cin >> n >> m >> p;

    if (p == 1) {
        std::cout << 0 << "\n";
        return;
    }
    Z::setMod(p);

    Matrix<Z> E(std::vector<std::vector<Z>>{
        {1, 0},
        {0, 1}
    });
    Matrix<Z> A(std::vector<std::vector<Z>>{
        {0, 1},
        {1, 1}
    });
    A = power(A, m);

    auto dfs = [&](auto &&self, i64 n) -> Matrix<Z> {
        if (n == 0) {
            return E;
        }
        return (n & 1) ? (E + power(A, n / 2 + 1)) * self(self, n / 2): (E + A * self(self, n - 1));
    };
    auto ans = dfs(dfs, n / m);

    std::cout << ans[1][0] << "\n";
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    std::cout << std::fixed << std::setprecision(12);
    
    int T = 1;
    std::cin >> T;
    
    while (T--) {
        solve();
    }
    
    return 0;
}

相关文章:

  • Minine源码设计逻辑解析
  • Cursor从小白到专家
  • DSP数字信号处理
  • 聊聊NAT:从内网到外网的数据旅程
  • HTML字符实体笔记
  • SSE流式FastAPI
  • STM32入门笔记(03): ADC 电阻分压的方式测量电池电压方案(避免浮点运算,改为整数运算)(SPL库函数版)(3)
  • vue3 获取当前路由信息失败问题
  • OpenCV DNN 模块使用指南
  • ToDesk云电脑各类鼠标有什么区别?虚拟/3D/游戏鼠标等各有利
  • 100道C#高频经典面试题及答案解析:C#程序员面试题库分类总结
  • pfsense部署三(snort各版块使用)
  • 探秘海螺 AI 视频与计算机视觉算法的奇妙融合
  • 95 计费 5% 时间窗口的利用
  • Java 双端队列实战 实现滑动窗口 用LinkedList的基类双端队列Deque实现 洛谷[P1886]
  • 在线运行vscode
  • 图解AUTOSAR_CP_EEPROM_Driver
  • Vue学习笔记集--Vuex
  • Androidstudio实现引导页文字动画
  • 大理石机械构件在设计的时候需要注意些什么?
  • 美联合健康集团高管枪杀案嫌疑人对谋杀指控不认罪
  • 全国首例!上市公司董监高未履行公开增持承诺,投资者起诉获赔
  • 农贸美学、业态再构、智能管理,今天的菜市场不止有菜
  • 今年五一,贵州一脸“爆相”
  • 独家丨前华金证券宏观首席秦泰加盟华福证券,任研究所副所长
  • 荣盛发展:拟以酒店、代建等轻资产板块业务搭建平台,并以其股权实施债务重组