前言
个人认为,本场比赛 E E E 题比 D D D 题简单一点,也好讲一点。所以我们先将 E E E 题再讲 D D D 题。
E - Sinking Land
题目大意:
你有一个大小为 n × m n \times m n×m 的岛屿,其中在第 i i i 行,第 j j j 列地区的海拔为 a i , j a_{i, j} ai,j,岛屿四面环海,岛屿的面积即为各区域面积之和,每个区域面积为 1 1 1。
从现在开始,海平面每年上升 1 1 1。当海平面大于等于地区的海拔高度时,所有临海的地区都会被淹没。
求·对于每个 i = 1 , 2 , 3 , 4 , . . . , Y i = 1, 2, 3, 4, ..., Y i=1,2,3,4,...,Y,求 i i i 年后,该岛屿的仍高于海平面的面积。
数据范围: 1 ≤ n , m ≤ 1000 1 \leq n, m \leq 1000 1≤n,m≤1000, 1 ≤ Y ≤ 1 0 5 1 \leq Y \leq 10^5 1≤Y≤105, 1 ≤ a i , j ≤ 1 0 5 1 \leq a_{i, j} \leq 10^5 1≤ai,j≤105
分析
1、只有临海地区的海拔低于等于海平面时,该地区才会被淹没。而初始时,只有第一行、第 n n n 行、第一列、第 m m m 列是临海的。所以我们将这些数据的存起来,我用的是 m a p map map,其键是地区的海拔,值则是一个 v e c t o r vector vector 里面存放的是同样是这个海拔的且临海的地区的坐标。
2、因为海平面是逐一上升的,所以对于每次海平面上升。我们都对临海且海拔等于海平面的地区做一次 B F S BFS BFS,将所有与当前地区相邻,并且海拔小于等于当前地区的地方都永久标记一下,标识当前地区已经被淹没了。
3、对于我们 B F S BFS BFS 遍历不到的地区,我们应该再次将其加入到我们之前的 m a p map map 里面。因为,即然当前地区是我第一个遍历不到的地区,那说明在遍历它之前的地区,都是可以遍历的。可以遍历就代表被淹没了,所以不可遍历的地区就变成了临海地区了。
是要实现了上述三个步骤,那么这题就引刃而解了。接下来上代码。
(
p
s
.
ps.
ps. 这题真的比
D
D
D 简单太多了,当时
D
D
D 做的脑壳都昏了,做完就只剩十几分钟了,火急火燎看完这题,构思编码最后五秒成功提交还是
W
A
WA
WA 了。嘻嘻,当时因为太急了,没有将新的临海地区加到
m
a
p
map
map 里去,所以以上三步缺一不可)
AC代码:
#include <bits/stdc++.h>
#define _1 first
#define _2 second
#define gps(x) std::fixed << std::setprecision(x)
using i64 = long long;
const int N = 1e3 + 5, M = 5e5 + 5;
const int mod = 998244353;
const i64 INF = 1e18;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int n, m, k;
int g[N][N];
bool st[N][N];
std::map<int, std::vector<std::pair<int, int>>> mp;
int bfs(int u, int v) {
int res = 1;
std::queue<std::pair<int, int>> pq;
pq.push({u, v});
st[u][v] = true;
while (pq.size()) {
auto now = pq.front(); pq.pop();
for (int i = 0; i < 4; i ++) {
int x = now._1 + dx[i], y = now._2 + dy[i];
if (x < 1 || x > n || y < 1 || y > m) continue ;
if (g[x][y] > g[u][v]) {
mp[g[x][y]].push_back({x, y}); // 新临海地区坐标
continue ;
}
if (st[x][y]) continue ;
res ++ ;
st[x][y] = true ;
pq.push({x, y});
}
}
return res;
}
void solve() {
std::cin >> n >> m >> k;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
std::cin >> g[i][j];
if (i == 1 || i == n || j == 1 || j == m) {
mp[g[i][j]].push_back({i, j}); // 临海地区坐标加到mp里去
}
}
}
int ans = n * m; // 初始面积
for (int i = 1; i <= k; i ++) {
if (mp[i].size() == 0) std::cout << ans << '\n'; // 如果一个海平面上升,并没有与之同样的海拔的地区,就说明没有淹没任何地区,没必要搜索,直接输出答案即可
else {
int del = 0;
for (int j = 0; j < mp[i].size(); j ++) {
if (st[mp[i][j]._1][mp[i][j]._2]) continue ; // 已经遍历过的就没必要在遍历了
del += bfs(mp[i][j]._1, mp[i][j]._2);
}
ans -= del;
std::cout << ans << '\n';
}
}
return ;
}
int main()
{
std::ios::sync_with_stdio(false);std::cout.tie(nullptr);std::cout.tie(nullptr);
int _ = 1; // std::cin >> (_);
while (_ --) {
solve();
}
return 0;
}
D - Palindromic Number
题目大意:
给你一个 n n n,让你求:从 0 0 0 开始的,第 n n n 个回文数字。
回文数字: 363 363 363、 12344321 12344321 12344321、 0 0 0。
数据范围: 1 ≤ n ≤ 1 0 18 1 \leq n \leq 10^{18} 1≤n≤1018
分析:
这题这数据无法暴力,想过
d
p
dp
dp,但是也不好处理,注意到样例:当
n
=
1
0
18
n = 10^{18}
n=1018 时,
a
n
s
=
90000000000000000000000000000000009
ans = 90000000000000000000000000000000009
ans=90000000000000000000000000000000009 共
35
35
35 位数字,那就在本机预暴力一遍吧。我暴力了
1
∼
9
1 \sim 9
1∼9 位数字中,各位回文数的数量,得到结果如下:
10
,
9
,
90
,
90
,
900
,
900
,
9000
,
9000
,
90000
10, 9, 90, 90, 900, 900, 9000, 9000, 90000
10,9,90,90,900,900,9000,9000,90000
剩下的位数也不好再暴力了,大胆预测一下,后面的数量将会是 90000 , 900000 , 900000 , 9000000 , . . . . 90000, 900000, 900000, 9000000, .... 90000,900000,900000,9000000,....
当这个数列的前 35 35 35 项加起来的时候,正好是大于 1 0 18 10^{18} 1018 的,则说明这个数列很大可能是正确的。
但其实不用预测也很容易算出来,当有
9
9
9 位数字时,我们可自主更改的位数是
(
9
+
1
)
/
2
(9 + 1) / 2
(9+1)/2 ,如下所示:
00000
∣
0000
00000|0000
00000∣0000
因为要形成回文数,所以右边的数字只能跟着我左边的变化而变化,为了方便说明,我们称左边部分从左至右分别为万、千、百、十、个。变化条件满足如下所说:
∙
\bullet
∙ 个位变化
10
10
10 次,则十位变化
1
1
1 次。
∙
\bullet
∙ 十位变化
10
10
10 次,则百位变化
1
1
1 次。
∙
\bullet
∙ 百位变化
10
10
10 次,则千位变化
1
1
1 次。
∙
\bullet
∙ 千位变化
10
10
10 次,则万位变化
1
1
1 次。
∙
\bullet
∙ 而万位只能变化
9
9
9 次,因为再变化一次就要进位了。
设
b
i
t
i
bit_{i}
biti 为
i
i
i 位数中有
b
i
t
i
bit_{i}
biti 个回文数,则
b
i
t
9
=
9
×
10
×
10
×
10
×
10
=
90000
bit_{9} = 9 \times 10 \times 10 \times 10 \times 10 = 90000
bit9=9×10×10×10×10=90000。如此计算可得到,当十位数时,如下划分:
00000
∣
00000
00000|00000
00000∣00000
同样计算出来 b i t 10 = 90000 bit_{10} = 90000 bit10=90000,则得到上述中序列的正确性,以及“我们可自主更改的位数是 ( 9 + 1 ) / 2 (9 + 1) / 2 (9+1)/2”这句话的正确性。
现在,我们只需要求出第
n
n
n 个回文的位数,在构造其数字即可。如何构造呢?以
9
9
9 位数为例:
00000
∣
0000
00000|0000
00000∣0000
我们依旧称左边部分从左至右分别为万、千、百、十、个。上述内容已经写出了每一位变化的效用,那就可以得到。
∙
\bullet
∙ 个位变化
1
1
1 次,需要用掉
1
1
1 个
n
n
n。
∙
\bullet
∙ 十位变化
1
1
1 次,需要用掉
10
10
10 个
n
n
n。
∙
\bullet
∙ 百位变化
1
1
1 次,需要用掉
100
100
100 个
n
n
n。
∙
\bullet
∙ 千位变化
1
1
1 次,需要用掉
1000
1000
1000 个
n
n
n。
∙
\bullet
∙ 千位变化
1
1
1 次,需要用掉
10000
10000
10000 个
n
n
n。
我们推出第 n n n 个回文是几位数时, n n n 已经变小了,我们只需要对于剩余的 n n n 对答案做修改即可。
( p s . ps. ps. 也不知道这样有没有讲清楚,这种纯数学 + + + 思路 + + + 代码能力的题确实很麻烦,需要花点时间。)
AC代码:
#include <bits/stdc++.h>
#define _1 first
#define _2 second
#define gps(x) std::fixed << std::setprecision(x)
using i64 = long long;
const int N = 50, M = 5e5 + 5;
const int mod = 998244353;
const i64 INF = 1e18;
i64 n, m, k;
i64 rec[N], bit[N];
void solve() {
std::cin >> n;
int idx = 0;
for (int i = 1; i <= 36; i ++) {
if (n <= rec[i]) {
idx = i;
break ;
}
}
n -= rec[idx - 1] + 1;
std::vector<int> ans(idx + 5, 0);
if (idx != 1) ans[1] = ans[idx] = 1; // 当1 == idx时,说明只有一位数,此时0也要算进去。
i64 now = bit[idx] / 9;
int l = 1, r = idx;
while (n) {
if (n >= now) {
if (l != r) { // 为了防止奇数位时,最中间那位多加了
ans[l] ++ ;
ans[r] ++ ;
}
else ans[l] ++ ;
n -= now;
}
else {
now /= 10;
l ++; r -- ;
}
}
for (int i = 1; i <= idx; i ++) {
std::cout << ans[i];
} std::cout << '\n';
return ;
}
void __init__() {
bit[1] = 10;
bit[2] = 9;
bit[3] = 90;
for (int i = 4; i <= 36; i ++) {
if (bit[i - 1] != bit[i - 2]) bit[i] = bit[i - 1];
else bit[i] = bit[i - 1] * 10;
}
for (int i = 1; i <= 36; i ++) {
rec[i] = rec[i - 1] + bit[i];
}
return ;
}
int main()
{
std::ios::sync_with_stdio(false);std::cout.tie(nullptr);std::cout.tie(nullptr);
__init__();
int _ = 1; // std::cin >> (_);
while (_ --) {
solve();
}
return 0;
}