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

news2025/3/24 21:46:59

比赛链接

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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2320317.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++继承机制:从基础到避坑详细解说

目录 1.继承的概念及定义 1.1继承的概念 1.2 继承定义 1.2.1定义格式 1.2.2继承关系和访问限定符 1.2.3继承基类成员访问方式的变化 总结&#xff1a; 2.基类和派生类对象赋值转换 3.继承中的作用域 4.派生类的默认成员函数 ​编辑 默认构造与传参构造 拷贝构造&am…

MySQL数据库精研之旅第二期:库操作的深度探索

专栏&#xff1a;MySQL数据库成长记 个人主页&#xff1a;手握风云 目录 一、查看数据库 二、创建数据库 2.1. 语法 2.2. 示例 三、字符集编码和校验(排序)规则 3.1. 查看数据库支持的字符集编码 3.2. 查看数据库支持的排序规则 3.3. 不同的字串集与排序规则对数据库的…

git_version_control_proper_practice

git_version_control_proper_practice version control&#xff0c;版本控制的方法之一就是打tag 因为多人协作的项目团队&#xff0c;commit很多&#xff0c;所以需要给重要的commit打tag&#xff0c;方便checkout&#xff0c;检出这个tag 参考行业的实践方式。如图git、linux…

计算机组成原理和计算机网络常见单位分类及换算

计算机组成原理&#xff08;主要用于存储、内存、缓存等&#xff09; 计算机网络&#xff08;主要用于传输速率&#xff09; 直观对比

【第二十八周】:Temporal Segment Networks:用于视频动作识别的时间分段网络

TSN 摘要Abstract文章信息引言方法时间分段采样分段聚合输入模态聚合函数多尺度时序窗口集成&#xff08;M-TWI&#xff09;训练 代码实现实验结果总结 摘要 本篇博客介绍了时间分段网络&#xff08;Temporal Segment Network, TSN&#xff09;&#xff0c;这是一种针对视频动…

扩展域并查集

什么叫扩展域并查集 1 和 2是敌人&#xff0c;那么就把1好12链接起来&#xff1a;表示1和2是敌人 2和11链接起来也是这个道理 然后2 和3使敌人同理。 最后12连接了1 和 3&#xff0c;表名1 和 3 是 2 的敌人&#xff0c;1和3 就是朋友 1.P1892 [BalticOI 2003] 团伙 - 洛谷 #in…

【C#语言】C#同步与异步编程深度解析:让程序学会“一心多用“

文章目录 ⭐前言⭐一、同步编程&#xff1a;单线程的线性世界&#x1f31f;1、寻找合适的对象✨1) &#x1f31f;7、设计应支持变化 ⭐二、异步编程&#xff1a;多任务的协奏曲⭐三、async/await工作原理揭秘⭐四、最佳实践与性能陷阱⭐五、异步编程适用场景⭐六、性能对比实测…

动态规划入门详解

动态规划&#xff08;Dynamic Programming&#xff0c;简称DP&#xff09;是一种算法思想&#xff0c;它将问题分解为更小的子问题&#xff0c;然后将子问题的解存起来&#xff0c;避免重复计算。 所以动态规划中每一个状态都是由上一个状态推导出来的&#xff0c;这一点就区别…

SOFABoot-09-模块隔离

前言 大家好&#xff0c;我是老马。 sofastack 其实出来很久了&#xff0c;第一次应该是在 2022 年左右开始关注&#xff0c;但是一直没有深入研究。 最近想学习一下 SOFA 对于生态的设计和思考。 sofaboot 系列 SOFABoot-00-sofaboot 概览 SOFABoot-01-蚂蚁金服开源的 s…

基于基于eFish-SBC-RK3576工控板的智慧城市边缘网关

此方案充分挖掘eFish-SBC-RK3576的硬件潜力&#xff0c;可快速复制到智慧园区、交通枢纽等场景。 方案亮点 ‌接口高密度‌&#xff1a;单板集成5GWiFi多路工业接口&#xff0c;减少扩展复杂度。‌AIoT融合‌&#xff1a;边缘端完成传感器数据聚合与AI推理&#xff0c;降低云端…

CSS基础知识一览

持续维护 选择器 display 常用属性 浮动 弹性布局

【免费】2000-2019年各省地方财政房产税数据

2000-2019年各省地方财政房产税数据 1、时间&#xff1a;2000-2019年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区、年份、地方财政房产税 4、范围&#xff1a;31省 5、指标说明&#xff1a;房产税是对个人和单位拥有的房产征收的一种…

车载以太网网络测试-21【传输层-DOIP协议-4】

目录 1 摘要2 DoIP entity status request/response&#xff08;0x4001、0x4002&#xff09;2.1 使用场景2.2 报文结构2.2.1 0x4001&#xff1a;DoIP entity status request2.2.2 0x4002&#xff1a;DoIP entity status response 3 Diagnostic power mode information request/…

Spring AI Alibaba ChatModel使用

一、对话模型&#xff08;Chat Model&#xff09;简介 1、对话模型&#xff08;Chat Model&#xff09; 对话模型&#xff08;Chat Model&#xff09;接收一系列消息&#xff08;Message&#xff09;作为输入&#xff0c;与模型 LLM 服务进行交互&#xff0c;并接收返回的聊天…

基于FPGA频率、幅度、相位可调的任意函数发生器(DDS)实现

基于FPGA实现频率、幅度、相位可调的DDS 1 摘要 直接数字合成器( DDS ) 是一种通过生成数字形式的时变信号并进行数模转换来产生模拟波形(通常为正弦波)的方法,它通过数字方式直接合成信号,而不是通过模拟信号生成技术。DDS主要被应用于信号生成、通信系统中的本振、函…

k8s高可用集群安装

一、安装负载均衡器 k8s负载均衡器 官方指南 1、准备三台机器 节点名称IPmaster-1192.168.1.11master-2192.168.1.12master-3192.168.1.13 2、在这三台机器分别安装haproxy和keepalived作为负载均衡器 # 安装haproxy sudo dnf install haproxy -y# 安装Keepalived sudo yum …

3DMAX曲线生成器插件CurveGenerator使用方法

1. 脚本功能简介 3DMAX曲线生成器插件CurveGenerator是一个用于 3ds Max 的样条线生成工具&#xff0c;用户可以通过简单的UI界面输入参数&#xff0c;快速生成多条样条线。每条样条线的高度值随机生成&#xff0c;且可以自定义以下参数&#xff1a; 顶点数量&#xff1a;每条…

六十天前端强化训练之第二十六天之Vue Router 动态路由参数大师级详解

欢迎来到编程星辰海的博客讲解 看完可以给一个免费的三连吗&#xff0c;谢谢大佬&#xff01; 目录 一、知识讲解 1. Vue Router 核心概念 2. 动态路由参数原理 3. 参数传递方案对比 二、核心代码示例 1. 完整路由配置 2. 参数接收组件 3. 导航操作示例 三、实现效果示…

Model Context Protocol:下一代AI系统集成范式革命

在2023年全球AI工程化报告中,开发者面临的核心痛点排名前三的分别是:模型与业务系统集成复杂度(58%)、上下文管理碎片化(42%)、工具调用标准化缺失(37%)。传统API集成模式在对接大语言模型时暴露明显短板:RESTful接口无法承载动态上下文,GraphQL缺乏工具编排能力,gR…

Java多线程与高并发专题——Future 是什么?

引入 在上一篇Callable 和 Runnable 的不同&#xff1f;的最后&#xff0c;我们有提到和 Callable 配合的有一个 Future 类&#xff0c;通过 Future 可以了解任务执行情况&#xff0c;或者取消任务的执行&#xff0c;还可获取任务执行的结果&#xff0c;这些功能都是 Runnable…