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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C语言】深入理解指针(二):从数组到二维数组的指针魔法

前言 在C语言中&#xff0c;指针一直是一个神秘而强大的存在。它不仅可以帮助我们高效地操作内存&#xff0c;还能让代码更加灵活和高效。今天&#xff0c;我们就来深入探讨指针的多种用法&#xff0c;从数组到二维数组&#xff0c;一步步揭开指针的神秘面纱。 一、数组名的指…

【MySQL】事务

目录 基本概念事务操作自动提交事务开启事务提交事务回滚事务代码示例 事务的特性 ACID事务的隔离级别读未提交 read uncommitted读已提交 read committed可重复读 repeatable read序列化&#xff08;串行&#xff09; serializable操作示例 基本概念 在 MySQL 中的事务&#…

flutter doctor提示cmdline-tools component is missing错误的解决

flutter doctor检测环境后报错如下: STEP1: 配置command-lines &#x1f4cc; 打开Androidstudio &#xff0c;找到sdkmanager &#x1f447; 安装command-line tools 如果找不到&#xff0c;记得打开右下角的「Show Package Details} 再次运行flutter doctor 即可正常 如…

iptables和netfilter内部报文处理

一、Iptables和netfilter 1.iptables基础 netfilter强大功能以及灵活性是通过iptables界面来实现。此命令行工具和它的前身ipchains语法相似&#xff1b;不过iptables使用netfilter子系统来增进网络连接、检验和处理方面的能力&#xff1b;ipchains使用错综复杂的规则集合来过…

[项目]基于FreeRTOS的STM32四轴飞行器: 十一.MPU6050配置与读取

基于FreeRTOS的STM32四轴飞行器: 十一.MPU6050 一.芯片介绍二.配置I2C三.编写驱动四.读取任务的测试 一.芯片介绍 芯片应该放置在PCB中间&#xff0c;X Y轴原点&#xff0c;敏感度131表示范围越小越灵敏。理想状态放置在地面上X&#xff0c;Y&#xff0c;Z轴为0&#xff0c;即…

JVM垃圾回收笔记01

文章目录 前言1. 如何判断对象可以回收1.1 引用计数法1.2 可达性分析算法查看根对象哪些对象可以作为 GC Root ?对象可以被回收&#xff0c;就代表一定会被回收吗&#xff1f; 1.3 引用类型1.强引用&#xff08;StrongReference&#xff09;2.软引用&#xff08;SoftReference…

解决Popwindow宽高的问题。

问题 在使用Popwindow进行自定义的过程中&#xff0c;需要设置popwindow的宽高。但是宽高很多时候容易出问题。比如下面的例子。 布局文件如下 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.andr…

Bell-1量子计算机分析:开启量子计算2.0时代的创新引擎

Bell-1量子计算机:开启量子计算2.0时代的创新引擎 一、引言 1.1 研究背景 在当今科技飞速发展的时代,量子计算作为前沿领域,正深刻地改变着科技格局,引领新一轮科技革命与产业变革。自 20 世纪 80 年代量子计算概念被提出以来,历经多年的理论探索与技术攻坚,已取得了众…

什么?中断禁用失效了?

什么&#xff1f;中断禁用失效了&#xff1f; 1. 前言 道友们&#xff0c;在嵌入式的开发中我们不管是RTOS或NO-RTOS的开发&#xff0c;都无法避免“多线程”的应用场景&#xff0c;高优先级的任务或中断打断低优先级的任务或中断&#xff0c;此时为了要保证共享数据的安全性…

单表达式倒计时工具:datetime的极度优雅(Kimi)

一个简单表达式&#xff0c;也可以优雅自成工具。 笔记模板由python脚本于2025-03-22 20:25:49创建&#xff0c;本篇笔记适合任意喜欢学习的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值&#xff1a;在于输出思考与经验&#xff0c;而不仅仅是知识的简单复述。 Pyth…

[笔记.AI]多头自注意力机制(Multi-Head Attention)

多头自注意力是深度学习领域&#xff0c;特别是自然语言处理&#xff08;NLP&#xff09;和Transformer模型中的关键概念。其发展源于对序列数据中复杂依赖关系的建模需求&#xff0c;特别是在Transformer架构的背景下。 举例 比喻-读长篇文章 用一个简单的比喻来理解“多头注…

SOFABoot-02-模块化隔离方案

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

【实用部署教程】olmOCR智能PDF文本提取系统:从安装到可视化界面实现

文章目录 引言系统要求1. 环境准备&#xff1a;安装Miniconda激活环境 2. 配置pip源加速下载3. 配置学术加速&#xff08;访问国外资源&#xff09;4. 安装系统依赖5. 安装OLMOCR6. 运行OLMOCR处理PDF文档7. 理解OLMOCR输出结果9. 可视化UI界面9.1 安装界面依赖9.2 创建界面应用…

asp.net core mvc模块化开发

razor类库 新建PluginController using Microsoft.AspNetCore.Mvc;namespace RazorClassLibrary1.Controllers {public class PluginController : Controller{public IActionResult Index(){return View();}} }Views下Plugin下新建Index.cshtml {ViewBag.Title "插件页…

边缘计算革命:重构软件架构的范式与未来

摘要 边缘计算通过将算力下沉至网络边缘&#xff0c;正在颠覆传统中心化软件架构的设计逻辑。本文系统分析了边缘计算对软件架构的范式革新&#xff0c;包括分布式分层架构、实时资源调度、安全防护体系等技术变革&#xff0c;并结合工业物联网、智慧医疗等场景案例&#xff0c…

单链表:数据结构的灵动之链

本文主要讲解链表的概念和结构以及实现单链表 目录 一、链表的概念及结构 二、单链表的实现 1.1链表的实现&#xff1a; 1.2单链表的实现&#xff1a; 单链表尾插&#xff1a; 单链表的头插&#xff1a; 单链表的尾删&#xff1a; 单链表头删&#xff1a; 单链表查找&#…

链表题型-链表操作-JS

一定要注意链表现在的头节点是空节点还是有值的节点。 一、移除链表中的元素 有两种方式&#xff0c;直接使用原来的链表进行删除操作&#xff1b;设置一个虚拟头节点进行删除操作。 直接使用原来的链表进行删除操作时&#xff0c;需要考虑是不是头节点&#xff0c;因为移除…

读《浪潮之巅》:探寻科技产业的兴衰密码

引言&#xff1a;邂逅《浪潮之巅》 在信息技术飞速发展的今天&#xff0c;科技公司如繁星般闪烁&#xff0c;又似流星般划过。而我与《浪潮之巅》的相遇&#xff0c;就像在浩渺的科技海洋中&#xff0c;发现了一座指引方向的灯塔。初次听闻这本书&#xff0c;是在一次技术交流会…

【和春笋一起学C++】文本文件I/O

在windows系统中读取键盘的输入和在屏幕上显示输出统称为&#xff1a;控制台输入/输出。把读取文本文件和把字符输出到文本文件中统称为&#xff1a;文本文件I/O。 目录 1. 输出文本文件 2. 读取文本文件 1. 输出文本文件 把字符输出到文本文件中和输出到控制台很相似&#x…

【C#】WinForm自定义控件及窗体

前言 WinForm&#xff08;Windows Forms&#xff09;是Microsoft.NET框架中的技术&#xff0c;用于开发Windows桌面应用程序。它提供了一套丰富的控件和组件。通过拖放控件、编写事件处理程序等方式快速构建用户界面。 通过属性窗口定制这些控件的外观和行为。 通过数据绑定&am…