Compare commits

...
3 Commits
Author SHA1 Message Date
hanyixuanten 67f1f4d5cc new file: AT_agc013_e.cpp 2026-09-16 19:39:01 +08:00
hanyixuanten 97ce969ae3 new file: solutions/CF1912E.md 2026-09-16 18:28:17 +08:00
hanyixuanten fefae6e22b new file: solutions/CF739A.md 2026-09-16 18:28:08 +08:00
4 changed files with 514 additions and 0 deletions
+98
View File
@@ -0,0 +1,98 @@
#include <bits/stdc++.h>
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;
}
+266
View File
@@ -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 <bits/stdc++.h>
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 <bits/stdc++.h>
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 <bits/stdc++.h>
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;
}
```
+111
View File
@@ -0,0 +1,111 @@
# CF1912E Evaluate It and Back Again题解
## 题意
给定两个数,要求你写出一个只包含数字, $+, \times, -$ 的式子,正着看是第一个数,反着看是第二个数。
## 题解
观察发现只有**一位数**和 $+, \times$ 的**单项式**倒过来读还是一样的,由 $-0$ 结尾的单项式倒过来读和原式结果相反。
发现可以将一个要求倒着读后数值不变的数表示为 $9$ 进制的形式,这样多项式中每一项都是倒着读后不变的。
- 对于两个奇偶性相同的数,可以求两个数的平均数,将 $p, q$ 分别表示为 $p = \overline{pq} + \frac{p - q}{2}, q = \overline{pq} - \frac{p - q}{2}$
- 对于两个奇偶性不同的数,也可以求平均数,但是为了让 $p, q$ 奇偶性统一,需要给 $p, q$ 分别减去一个正着读是奇数,反着读是偶数的数,如 $12$,然后即可按照第一种形式做
然后就是注意特判两个相同的数和 `0 0` 的情况,别忘记开 long long 就结束了
不懂看注释
## 参考代码
```cpp
#include <bits/stdc++.h>
using namespace std;
string ans;
string to_9(long long n) //转换为正反均相同的多项式
{
bool flag = n >= 0;
if (!flag)
n = -n;
ans = "";
long long cnt = 0;
long long yu[20] = {};
while (n >= 10ll)
yu[cnt++] = n % 9ll, n /= 9ll;
yu[cnt] = n;
if (!flag)
ans += "0-";
for (long long i = 0; i <= cnt; ++i)
{
if (yu[cnt - i] != 0)
{
for (long long j = 0; j < cnt - i; ++j)
ans += "9*";
ans += yu[cnt - i] + '0';
if (flag)
ans += "+0+";
else
ans += "-0-";
}
}
while (ans[ans.size() - 1] == '+' || ans[ans.size() - 1] == '-')
ans.pop_back(); // 去掉多于符号
return ans;
}
string to_9_0(long long n) // 转换为正反读相反的多项式
{
bool flag = n >= 0;
if (!flag)
n = -n;
ans = "";
long long cnt = 0;
long long yu[20] = {};
while (n >= 10)
{
yu[cnt++] = n % 9ll;
n /= 9ll;
}
yu[cnt] = n;
if (!flag)
ans += "0-";
for (long long i = 0; i <= cnt; ++i)
{
if (yu[cnt - i] != 0)
{
for (long long j = 0; j < cnt - i; ++j)
ans += "9*";
ans += yu[cnt - i] + '0';
if (flag)
ans += "-0+";
else
ans += "+0-";
}
}
while (ans[ans.size() - 1] == '+' || ans[ans.size() - 1] == '-')
ans.pop_back(); // 去掉多于符号
return ans;
}
int main()
{
long long p = 0, q = 0;
scanf("%lld%lld", &p, &q);
if (abs(p) % 2 == abs(q) % 2) // 同奇偶
{
long long _pq = p + q >> 1, p_q = p - _pq;
if (p == 0 && q == 0) // 特判
{
puts("0");
return 0;
}
cout << to_9(_pq) << ((_pq != 0 && p_q != 0) ? "+" : "") << to_9_0(p_q) << endl;
}
else // 异奇偶
{
long long _pq = (p - 21 + q - 12) >> 1, p_q = p - 21 - _pq; // 转换为同奇偶
cout << to_9(_pq) << ((_pq != 0 && p_q != 0) ? "+" : "") << to_9_0(p_q) << ((p_q != 0 || _pq != 0) ? "+" : "") << "21" << endl;
}
return 0;
}
```
+39
View File
@@ -0,0 +1,39 @@
# [CF739A Alyona and mex](https://www.luogu.com.cn/problem/CF739A)
## 题意
给定 $m$ 个区间,构造出一个长度为 $n$ 的序列,使得这 $m$ 个区间的最小 $mex$ 最大。 $mex$ 定义为最小的没有出现过的自然数。
## 题解
先观察样例,发现两次输出的 $mex$ 均等于**最短的区间长度**,显然这不是巧合。
首先很容易得出 $mex(S) \le \lvert S \rvert$,且当 $S$ 覆盖 $0$ 到 $\lvert S \rvert - 1$ 时取等,所以最终答案不会超过最短的区间的长度,然后思考如何构造出答案等于最短区间长度的情况。
求出最短的区间长度为 $x$,只要让这个区间完全覆盖 $0, 1, 2, ..., x - 1$ ,就可以取到 $mex$ 的最大值,所以则只要在数组中循环填入 $0, 1, 2, ..., x - 1$,就能保证每一个长度大于等于 $x$ 的区间 $mex$ 一定都等于 $x$,因为每一个长度大于等于 $x$ 的区间都至少覆盖一次 $0, 1, 2, ..., x - 1$。(可以自己手搓几个样例试试)
## 代码
```cpp
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, m;
scanf("%d%d", &n, &m);
int minlen = INT_MAX;
for (int i = 0; i < m; ++i)
{
int l, r;
scanf("%d%d", &l, &r);
minlen = min(minlen, r - l + 1);
}
printf("%d\n", minlen);
int cnt = 0;
for (int i = 1; i <= n; ++i)
printf("%d ", i % minlen);
return 0;
}
```
---