From 67f1f4d5ccb0d895e96b553661c750231f137d32 Mon Sep 17 00:00:00 2001 From: hanyixuanten <105723997+hanyixuanten@users.noreply.github.com> Date: Wed, 16 Sep 2026 19:39:01 +0800 Subject: [PATCH] new file: AT_agc013_e.cpp --- AT_agc013_e.cpp | 98 +++++++++++++++ solutions/AT_agc013_e.md | 266 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 364 insertions(+) create mode 100644 AT_agc013_e.cpp create mode 100644 solutions/AT_agc013_e.md diff --git a/AT_agc013_e.cpp b/AT_agc013_e.cpp new file mode 100644 index 0000000..1cb62cf --- /dev/null +++ b/AT_agc013_e.cpp @@ -0,0 +1,98 @@ +#include +using namespace std; +const int mod = 1e9 + 7; +int n, m; +int a[100005]; +const long long fir[4][2] = {{0, 0}, {0, 1}, {0, 2}, {0, 1}}; +const long long yes[4][4] = {{0, 0, 0, 0}, {0, 1, 0, 0}, {0, 2, 1, 0}, {0, 1, 1, 1}}; +const long long no[4][4] = {{0, 0, 0, 0}, {0, 1, 0, 1}, {0, 2, 1, 2}, {0, 1, 1, 2}}; +struct Mat +{ + long long a[5][5] = {}; + int n, m; + Mat(int _n = 0, int _m = 0, int cpy = 0) : n(_n), m(_m) + { + if (cpy == 1) + for (int i = 1; i <= 3; ++i) + a[i][1] = fir[i][1]; + else if (cpy == 2) + for (int i = 1; i <= 3; ++i) + for (int j = 1; j <= 3; ++j) + a[i][j] = yes[i][j]; + else if (cpy == 3) + for (int i = 1; i <= 3; ++i) + for (int j = 1; j <= 3; ++j) + a[i][j] = no[i][j]; + } + void operator*=(const Mat &b) + { + if (m != b.n) + exit(-1); + long long res[5][5] = {}; + for (int i = 1; i <= n; ++i) + for (int j = 1; j <= b.m; ++j) + for (int k = 1; k <= m; ++k) + res[i][j] = (res[i][j] + a[i][k] * b.a[k][j]) % mod; + for (int i = 1; i <= n; ++i) + for (int j = 1; j <= b.m; ++j) + a[i][j] = res[i][j]; + m = b.m; + } + Mat operator*(const Mat &b) + { + if (m != b.n) + exit(-1); + Mat res(n, b.m); + for (int i = 1; i <= n; ++i) + for (int j = 1; j <= b.m; ++j) + for (int k = 1; k <= m; ++k) + res.a[i][j] = (res.a[i][j] + a[i][k] * b.a[k][j]) % mod; + return res; + } +}; + +Mat qpow(Mat a, long long k) +{ + Mat res(a.n, a.n); + for (int i = 1; i <= a.n; ++i) + res.a[i][i] = 1; + while (k) + { + if (k & 1) + res *= a; + a *= a; + k >>= 1; + } + return res; +} +Mat yess(3, 3, 2); +Mat noo(3, 3, 3); + +int main() +{ + scanf("%d%d", &n, &m); + for (int i = 1; i <= m; ++i) + scanf("%d", &a[i]); + Mat now(3, 1, 1); + int pos = 1; + for (int i = 1; i <= m; ++i) + { + int x = a[i]; + if (x < pos) + continue; + if (x > pos) + { + now = qpow(noo, x - pos) * now; + } + { + now = yess * now; + } + pos = x + 1; + } + if (n - pos > 0) + { + now = qpow(noo, n - pos) * now; + } + printf("%lld\n", now.a[3][1]); + return 0; +} \ No newline at end of file diff --git a/solutions/AT_agc013_e.md b/solutions/AT_agc013_e.md new file mode 100644 index 0000000..db8be77 --- /dev/null +++ b/solutions/AT_agc013_e.md @@ -0,0 +1,266 @@ +# AT_agc013_e + +[luoshunran](https://www.acwing.com/file_system/file/content/whole/index/content/14321537/) + +## 20 pts O(n) + +把正方形的交界处看作隔板,在每两个隔板之间放入一黑一白两个球,正方形的大小也就等于区间内放入两个球的方案数,正方形的面积的乘积就是所有的放球方案乘法原理乘起来。于是变成了求有多少种放隔板、放球的方案。 + +做 dp $f(i, 0/1/2)$,代表目前这个区间内放入了几个球的方案。 + +注意 $f(1, 1) = 2$ + +若 $i - i + 1$ 之间可以放隔板, + +- $f(i+1, 0) = f(i, 0) + f(i, 2)$ +- $ f(i+1, 1) = 2 * f(i, 0) + f(i, 1) + 2 * f(i, 2)$, 这里乘以 2 代表决策放入的是黑球还是白球 +- $ f(i+1, 2) = f(i, 0) + f(i, 1) + 2 * f(i, 2) $ + +类似地,也可以列出不可以放隔板的情况。 + +- $f(i+1, 0) = f(i, 0)$ +- $f(i+1, 1) = 2 * f(i, 0) + f(i, 1)$ +- $f(i+1, 2) = f(i, 0) + f(i, 1) + f(i, 2)$ + +```cpp +#include +using namespace std; +const int mod = 1e9 + 7; +int n, m; +long long f[5000005][3]; +int a[100005]; +int main() +{ + scanf("%d%d", &n, &m); + for (int i = 1; i <= m; ++i) + scanf("%d", &a[i]); + sort(a + 1, a + m + 1); + int tot = 1; + f[1][0] = 1, f[1][1] = 2, f[1][2] = 1; + for (int i = 1; i < n; ++i) + { + if (i == a[tot]) + { + tot++; + f[i + 1][0] = f[i][0]; + f[i + 1][1] = (f[i][0] * 2 + f[i][1]) % mod; + f[i + 1][2] = (f[i][0] + f[i][1] + f[i][2]) % mod; + } + else + { + f[i + 1][0] = (f[i][0] + f[i][2]) % mod; + f[i + 1][1] = (f[i][0] * 2 + f[i][1] + 2 * f[i][2]) % mod; + f[i + 1][2] = (f[i][0] + f[i][1] + f[i][2] * 2) % mod; + } + } + printf("%lld\n", f[n][2]); + return 0; +} +``` + +## 90 pts O(m log n) + +注意到一个区间内需要多次转移,非常浪费。可以考虑使用矩阵快速幂优化。 + +对于非标记点,转移矩阵为: + +$$ +T=\begin{bmatrix} + 1&0&1\\ + 2&1&2\\ + 1&1&2 +\end{bmatrix} +$$ + +对于标记点: + +$$ +T=\begin{bmatrix} + 1&0&0\\ + 2&1&0\\ + 1&1&1 +\end{bmatrix} +$$ + +初始位置矩阵为: + +$$ +T=\begin{bmatrix} + 1\\ + 2\\ + 1 +\end{bmatrix} +$$ + +矩阵快速幂代码可参考[P1962](https://luogu.com.cn/problem/P1962) + +```cpp +#include +using namespace std; +const long long MOD = 1000000007LL; +struct Mat { + long long a[105][105]; + int n, m; + Mat(int _n = 0, int _m = 0) : n(_n), m(_m) { + memset(a, 0, sizeof(a)); + } + void operator*=(const Mat &b) { + if (m != b.n) { + exit(-1); + } + long long res[105][105] = {}; + for (int i = 1; i <= n; ++i) { + for (int j = 1; j <= b.m; ++j) { + for (int k = 1; k <= m; ++k) { + res[i][j] = (res[i][j] + a[i][k] * b.a[k][j]) % MOD; + } + } + } + for (int i = 1; i <= n; ++i) { + for (int j = 1; j <= b.m; ++j) { + a[i][j] = res[i][j]; + } + } + m = b.m; + } +}; + +Mat qpow(Mat a, long long k) { + Mat res(a.n, a.n); + for (int i = 1; i <= a.n; ++i) { + res.a[i][i] = 1; + } + while (k) { + if (k & 1) res *= a; + a *= a; + k >>= 1; + } + return res; +} + +int main() { + long long n; + scanf("%lld", &n); + + if (n <= 2) { + printf("1\n"); + return 0; + } + + Mat base1(1, 2), base2(2, 2); + + base1.a[1][1] = 1; + base1.a[1][2] = 1; + base2.a[1][1] = 1; + base2.a[1][2] = 1; + base2.a[2][1] = 1; + base2.a[2][2] = 0; + base2 = qpow(base2, n - 2); + base1 *= base2; + printf("%lld\n", base1.a[1][1]); + return 0; +} +``` + +最终程序: + +```cpp +#include +using namespace std; +const int mod = 1e9 + 7; +int n, m; +int a[100005]; +const long long fir[4][2] = {{0, 0}, {0, 1}, {0, 2}, {0, 1}}; +const long long yes[4][4] = {{0, 0, 0, 0}, {0, 1, 0, 0}, {0, 2, 1, 0}, {0, 1, 1, 1}}; +const long long no[4][4] = {{0, 0, 0, 0}, {0, 1, 0, 1}, {0, 2, 1, 2}, {0, 1, 1, 2}}; +struct Mat +{ + long long a[5][5] = {}; + int n, m; + Mat(int _n = 0, int _m = 0, int cpy = 0) : n(_n), m(_m) + { + if (cpy == 1) + for (int i = 1; i <= 3; ++i) + a[i][1] = fir[i][1]; + else if (cpy == 2) + for (int i = 1; i <= 3; ++i) + for (int j = 1; j <= 3; ++j) + a[i][j] = yes[i][j]; + else if (cpy == 3) + for (int i = 1; i <= 3; ++i) + for (int j = 1; j <= 3; ++j) + a[i][j] = no[i][j]; + } + void operator*=(const Mat &b) + { + if (m != b.n) + exit(-1); + long long res[5][5] = {}; + for (int i = 1; i <= n; ++i) + for (int j = 1; j <= b.m; ++j) + for (int k = 1; k <= m; ++k) + res[i][j] = (res[i][j] + a[i][k] * b.a[k][j]) % mod; + for (int i = 1; i <= n; ++i) + for (int j = 1; j <= b.m; ++j) + a[i][j] = res[i][j]; + m = b.m; + } + Mat operator*(const Mat &b) + { + if (m != b.n) + exit(-1); + Mat res(n, b.m); + for (int i = 1; i <= n; ++i) + for (int j = 1; j <= b.m; ++j) + for (int k = 1; k <= m; ++k) + res.a[i][j] = (res.a[i][j] + a[i][k] * b.a[k][j]) % mod; + return res; + } +}; + +Mat qpow(Mat a, long long k) +{ + Mat res(a.n, a.n); + for (int i = 1; i <= a.n; ++i) + res.a[i][i] = 1; + while (k) + { + if (k & 1) + res *= a; + a *= a; + k >>= 1; + } + return res; +} +Mat yess(3, 3, 2); +Mat noo(3, 3, 3); + +int main() +{ + scanf("%d%d", &n, &m); + for (int i = 1; i <= m; ++i) + scanf("%d", &a[i]); + Mat now(3, 1, 1); + int pos = 1; + for (int i = 1; i <= m; ++i) + { + int x = a[i]; + if (x < pos) + continue; + if (x > pos) + { + now = qpow(noo, x - pos) * now; + } + { + now = yess * now; + } + pos = x + 1; + } + if (n - pos > 0) + { + now = qpow(noo, n - pos) * now; + } + printf("%lld\n", now.a[3][1]); + return 0; +} +```