比赛链接
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(12⋅T)
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 1≤∑n≤106。
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∣=∣Vi∣−1(树中拆出来的连通块的性质)。
定义 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], 0≤i,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[i⊕u][j⊕v]=c[i⊕u][j⊕v]+a[i][j]×b[u][v], 0≤i,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 i⊕u 和 j ⊕ v j \oplus v j⊕v。
- 取反 ! ! !:也是重新定义,对于 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][j⊕1]=a[i][j], 0≤i,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(24∑n)
- 常数会稍大, 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:=li−1,要么令 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 1≤n≤100;
- 0 ≤ m ≤ 2025 0 \leq m \leq 2025 0≤m≤2025;
- 1 ≤ a i , l i , r i ≤ 1 0 9 1 \leq a_i, l_i, r_i \leq 10^9 1≤ai,li,ri≤109。
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=1⋃n[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 i∈C,使得 r i = min k ∈ C r k r_i = \min\limits_{k \in C}r_k ri=k∈Cminrk,此时将 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:=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,那我们可以找到一个 i ∈ C i \in C i∈C,使得 l i = max k ∈ C l k l_i = \max\limits_{k \in C}l_k li=k∈Cmaxlk,此时将 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:=sl−cl,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)=⎩ ⎨ ⎧li−d,0,d−ri,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=1∑n[m∣i]⋅fi.
其中 m ∣ i m \mid i m∣i 表示 m m m 可以整除 i i i,例如 2 ∣ 4 2 \mid 4 2∣4; f i f_i fi 表示斐波那契数列的第 i i i 项。
数据范围
- 1 ≤ T ≤ 1 0 5 1 \leq T \leq 10^5 1≤T≤105;
- 1 ≤ n , m ≤ 1 0 18 1 \leq n, m \leq 10^{18} 1≤n,m≤1018;
- 1 ≤ p ≤ 1 0 9 1 \leq p \leq 10^9 1≤p≤109。
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=1∑n[m∣i]⋅fi=i=1∑⌊mn⌋fi⋅m=i=1∑⌊mn⌋Fi⋅m[0]=i=1∑⌊mn⌋(F0×Ai⋅m)[0]=(F0i=1∑⌊mn⌋(Am)i)[0]=([0,1]i=1∑⌊mn⌋(Am)i)[0]=i=1∑⌊mn⌋(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=1∑⌊mn⌋Bi 后,再取其第 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=1∑⌊mn⌋Bi=i=0∑⌊mn⌋Bi。
下设 N = ⌊ n m ⌋ N = \lfloor \frac{n}{m} \rfloor N=⌊mn⌋,我们考虑分治求解 ∑ i = 0 N B i \sum\limits_{i = 0}^{N}B^i i=0∑NBi。
- 当 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+⋯+A⌊2N⌋,A⌊2N⌋+1+A⌊2N⌋+2+⋯+AN. 而把下面提一个 A ⌊ N 2 ⌋ + 1 A^{\lfloor \frac{N}{2} \rfloor + 1} A⌊2N⌋+1 就可以得到上面,所以直接算前一半,最后乘以 ( E + A ⌊ N 2 ⌋ + 1 ) (E + A^{\lfloor \frac{N}{2} \rfloor + 1}) (E+A⌊2N⌋+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+⋯+AN−1), 后面的 ( E + A + A 2 + ⋯ + A N − 1 ) (E + A + A^2 + \cdots + A^{N - 1}) (E+A+A2+⋯+AN−1) 就是 N − 1 N - 1 N−1 为奇数的情况。
时间复杂度 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+log⌊mn⌋))=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;
}