AtCoder Beginner Contest 357
AtCoder Beginner Contest 357
C - Sierpinski carpet BFS
Problem Statement
For a non-negative integer \(K\), we define a level-\(K\) carpet as follows:
- A level-\(0\) carpet is a \(1 \times 1\) grid consisting of a single black cell.
- For \(K \gt 0\), a level-\(K\) carpet is a \(3^K \times 3^K\) grid. When this grid is divided into nine \(3^{K-1} \times 3^{K-1}\) blocks:
- The central block consists entirely of white cells.
- The other eight blocks are level-\((K-1)\) carpets.
You are given a non-negative integer \(N\).
Print a level-\(N\) carpet according to the specified format.
问题陈述
对于一个非负整数 \(K\) ,我们定义一个等级地毯 \(K\) 如下:
- 一级- \(0\) 地毯是由一个黑色单元格组成的 \(1 \times 1\) 网格。
- 对于 \(K \gt 0\) 来说, \(K\) 级地毯是一个 \(3^K \times 3^K\) 网格。当这个网格被划分为九个 \(3^{K-1} \times 3^{K-1}\) 块时:
- 中央区块完全由白色单元格组成。
- 其他八个区块是 \((K-1)\) 级地毯。
给你一个非负整数 \(N\) 。
请按照指定格式打印 \(N\) 级地毯。
Constraints
- \(0 \leq N \leq 6\)
- \(N\) is an integer.
Output
Print \(3^N\) lines.
The \(i\)-th line (\(1 \leq i \leq 3^N\)) should contain a string \(S_i\) of length \(3^N\) consisting of .
and #
.
The \(j\)-th character of \(S_i\) (\(1 \leq j \leq 3^N\)) should be #
if the cell at the \(i\)-th row from the top and \(j\)-th column from the left of a level-\(N\) carpet is black, and .
if it is white.
Sample Output 1
###
#.#
###
A level-\(1\) carpet is a \(3 \times 3\) grid as follows:
When output according to the specified format, it looks like the sample output.
Solution
对于一个N级地毯,由题我们可以将其分成九个小块,每个小块为\(3^{N-1} \times 3^{N-1}\) ,中心块固定全为.
,周围的八个小块,每一块都是由N-1级地毯构成,那一个N-1级地毯又可以分成八个N-2级地毯和一个全为.
的中心块,就这么对每一子块划分下去,最后会划分到1级地毯,他的八个子块为0级地毯也就是八个#
,一个中心块.
,至此就划分完毕。
很明显我们使用递归的方法做,不断划分子块,直到其为0级。递归处理的每一层我们要先对当前块的中心块做处理,然后再向下搜索其它八个子块。
利用矩阵存储,对每个子块用其左上角坐标搜索,方便找他的中心块
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
char g[800][800]; //存答案矩阵
int a[10]; //a[0]~a[n] 存3的0~n次幂的结果
int n;
void dfs(int u, int x, int y)
{
if(u == 0) return; //u=0时只有一个点 为# 不用改
//对一个大块来说,将其分为九个小块,中间的块全变为'.',其余周围八块继续向下搜索
//对当前搜到的块 将其中心块全部改成'.' ,分成的八个小块的长和宽均为a[u - 1],中心宽的左上角坐标为(x + a[u - 1], y + a[u - 1])
for (int i = x + a[u - 1]; i < x + a[u - 1] * 2; i ++ )
for (int j = y + a[u - 1]; j < y + a[u - 1] * 2; j ++ )
{
g[i][j] = '.';
}
//搜其余八个小块,利用其左上角坐标向下搜索
for (int i = 0; i < 3; i ++ )
for (int j = 0; j < 3; j ++ )
{
if (i == 1 && j == 1) continue; //中心块的情况,上面已经处理过了,跳过
dfs(u - 1, x + i * a[u - 1], y + j * a[u - 1]); //用子块的左上角坐标,向下搜每个子块的情况
}
}
void solve()
{
cin >> n;
a[0] = 1;
for(int i = 1; i <= 6; i ++ ) a[i] = a[i - 1] * 3;
//先将答案矩阵内所有位置置为'#',后面我们就只需对其中数量较少的'.'做更改
for (int i = 0; i < a[n]; i ++ )
{
for (int j = 0; j < a[n]; j ++ )
{
g[i][j] = '#';
}
}
//对其中'.'的部分进行更改
dfs(n, 0, 0);
//输出答案
for (int i = 0; i < a[n]; i ++ )
{
for (int j = 0; j < a[n]; j ++ )
{
cout << g[i][j];
}
cout << endl;
}
return;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
return 0;
}
D - 88888888 数论
Problem Statement
For a positive integer \(N\), let \(V_N\) be the integer formed by concatenating \(N\) exactly \(N\) times.
More precisely, consider \(N\) as a string, concatenate \(N\) copies of it, and treat the result as an integer to get \(V_N\).
For example, \(V_3=333\) and \(V_{10}=10101010101010101010\).
Find the remainder when \(V_N\) is divided by \(998244353\).
问题陈述
对于正整数 \(N\) ,设 \(V_N\) 是由 \(N\) 恰好连接 \(N\) 次所组成的整数。
更确切地说,把 \(N\) 看作一个字符串,连接它的 \(N\) 份,并把结果看作一个整数,得到 \(V_N\) 。
例如, \(V_3=333\) 和 \(V_{10}=10101010101010101010\) 。
求 \(V_N\) 除以 \(998244353\) 的余数。
Constraints
- \(1 \leq N \leq 10^{18}\)
- \(N\) is an integer.
Sample Input 2
9
Sample Output 2
1755646
The remainder when \(V_9=999999999\) is divided by \(998244353\) is \(1755646\).
Solution
\(V_N\) 是由 \(N\) 恰好连接 \(N\) 次所组成的整数,例如\(V_9=999999999\),其实我们可以发现可以变成\(V_9 = 9*10^0 + 9*10^1 + ... + 9*10^9\),然后我们把9提出来里面其实是一个等比数列\(V_9=9*(1 + 10 + 100 + ... +10^9)=9* a_1\frac{1-q^n}{1-q} = 9*\frac{10^9-1}{9}\)
推广到一般情况
\(V_n=n*\frac{10^n-1}{9}\)
注意此时我们只写出了N只有1位的情况
如果是\(V_{10}=10101010101010101010\)该怎么表示呢 其实就是把高位1和低位0捆起来,一起前进,每一位都直接前进两位
\(V_{10}=10 + 10*10^{2*1} + 10*10^{2*2}+...10*10^{2*8}\)
推广到一般形式 k为N的位数
\(V_N=N+N*10^{k*1}+N*10^{k*2}...+N*10^{k*(N-1)}\)
N提出来
\(V_N=N*(1+10^{k*1}+...+10^{k*(N-1)})=N* \frac{10^{k*N}-1}{10^k-1}\)
公式推导完成,由于本题要模上\(998244353\) 我们采用乘法逆元计算除法
用快速幂求\(10^{kN}\)
---悲伤分割线
code一直通不过 一直WA+TLE
原因就在这串代码 这里是算\(N*(10^{k*N}-1)\)
ans = t * (qmi(qmi(10, k), n) - 1) % mod;
这里刚开始写成t * (10, k * n) - 1) % mod 造成TLE和WA debug半天 在这种数据很大的题内对乘法一定要小心再小心 一不小心就会狠狠溢出
TLE的原因是快速幂内有一个while循环 k * n数据过大循环次数过多 就TLE了 WA原因也出在这里 k * n 可能溢出了(还是longlong范围 想想得多吓人) 我们需要将10^{k*n}拆开写成qmi(qmi(10, k), n) 做两次快速幂 这样就不会超时 在qmi()内也有取模也不会WA
总结两条经验
-
数据很大的题里一定得注意随时取模 可以将步骤拆开 分开取模 最后再乘
-
特别特别注意乘法 两个数量级都很大的数一乘很容易溢出
-
//ans *= qmi(tmp, mod - 2) % mod; //将除法换成做乘法逆元 ans = ans * qmi(tmp, mod - 2) % mod; //将除法换成做乘法逆元
-
刚开始这行语句写成上面的形式 也溢出了 因为直接写*=就起不到对结果ans * qmi(tmp, mod - 2)取模的作用了
- 所以以后数据很大的题 一定不要写*= 导致取模失败
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 998244353;
int n;
int ans;
//快速幂求幂和逆元
int qmi(int x,int k)
{
int res = 1;
while (k > 0)
{
if (k % 2) res = (res * x) % mod;
x = (x * x) % mod;
k = k / 2;
}
return res;
}
int getLen(int x)
{
int res = 0;
while (x > 0)
{
x /= 10;
res ++ ;
}
return res;
}
void solve()
{
cin >> n;
int k = getLen(n); //找到n的位数
int t = n % mod; //先将n取模 避免后面乘法溢出
//cout << k << endl;
ans = t * (qmi(qmi(10, k), n) - 1) % mod; //这里刚开始写成t * (10, k*n) - 1) % mod 造成TLE和WA
//cout << ans << endl;
int tmp = (qmi(10, k) - 1) % mod;
ans = ans * qmi(tmp, mod - 2) % mod; //将除法换成做乘法逆元
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
return 0;
}