ABC353 A-E题解
- A
- 题目
- AC Code(CPP)
- AC Code(Python)
- B
- 题目
- AC Code(CPP)
- AC Code(Python)
- C
- 题目
- AC Code(CPP)
- AC Code(Python)
- D
- 题目
- AC Code(CPP)
- AC Code(Python)
- E
- 题目
- AC Code(CPP)
- AC Code(Python)
下面的内容不包括题目翻译,要想获取题目翻译,请参照 这篇教程 来获取题目翻译。
A
题目
从左到右遍历数组,找到第一个比数组第一个元素大的元素,输出坐标即可。如果没有输出 -1
即可。
AC Code(CPP)
#include <bits/stdc++.h>
using namespace std;
int n, h[200100];int main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n;for (int i = 1; i <= n; i++) {cin >> h[i];if (h[i] > h[1]) {cout << i << '\n';return 0;}}cout << -1;return 0;
}
AC Code(Python)
n = int(input())
a = [int(i) for i in input().split()]
flag = 0for i in range(len(a)):if a[i] > a[0]:print(i + 1)flag = 1break
print(("-1", "")[flag])
B
题目
就是个模拟,按照题目所给信息编码即可。
AC Code(CPP)
#include <bits/stdc++.h>
using namespace std;
int n, k, a[200100];int main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n >> k;for (int i = 1; i <= n; i++) {cin >> a[i];}int cnt = 1, tmp = 0;for (int i = 1; i <= n; i++) {if (a[i] <= k - tmp) {tmp += a[i];}else {tmp = a[i];cnt++;}}cout << cnt;return 0;
}
AC Code(Python)
n, k = [int(i)for i in input().split()]
a = [int(i)for i in input().split()]
cnt = 0
Sum = 0
for i in a:if Sum + i > k:Sum = 0cnt += 1Sum += i
print(cnt + 1)
C
题目
可以发现,无论数组如何排序,每一个 A i A_i Ai 都在一个 形如 ( A x , A y ) (A_x,A_y) (Ax,Ay) 的对中,所以数组的顺序并不重要。
因为所有 A i ≤ 1 0 8 A_i\le 10^8 Ai≤108,所以 A i + A j A_i + A_j Ai+Aj 除以 1 0 8 10^8 108 的余数要么是 A i + A j − 1 0 8 ( A i + A j > 1 0 8 ) A_i+A_j - 10^8(A_i+A_j>10^8) Ai+Aj−108(Ai+Aj>108),要么是 A i + A j ( A i + A j < 1 0 8 ) A_i + A_j(A_i+A_j < 10^8) Ai+Aj(Ai+Aj<108)
可以将数组排序,发现对于每一个 A i A_i Ai,如果 A i + A j > 1 0 8 A_i+A_j>10^8 Ai+Aj>108,那么 A i + A j + 1 A_i+A_{j+1} Ai+Aj+1 定然大于 1 0 8 10^8 108。发现满足单调性,可以二分,然后用前缀和求出后面所有的和,超过 1 0 8 10^8 108 的段减去相应个数的 1 0 8 10^8 108,在知道分界线坐标后,上述所有操作都可以在 O ( 1 ) O(1) O(1) 的时间复杂度内求出,加上二分和排序的时间复杂度就是 O ( N log 2 N ) O(N\log_2N) O(Nlog2N),十分优秀。
此题使用 Python
实现的代码耗时 1293ms,隔壁 C++
只用了 43ms,30倍的时间差距不容小觑。在用 Python
实现代码是一定要注意常数问题,因为这个语言本来就慢。
AC Code(CPP)
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, a[300100];
int sum[300100], ans;signed main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n;for (int i = 1; i <= n; i++) {cin >> a[i];}sort(a + 1, a + n + 1);for (int i = 1; i <= n; i++) {sum[i] = sum[i - 1] + a[i];}a[n + 1] = 100000001;for (int i = 1; i < n; i++) {int bas = 100000000 - a[i];int l = lower_bound(a + i + 1, a + n + 1, bas) - a - 1;ans += sum[n] - sum[i] - (n - l) * 100000000 + a[i] * (n - i);}cout << ans << '\n';return 0;
}
AC Code(Python)
n = int(input())
a = [int(i) for i in input().split()]
a.sort()
Sum = [a[0]]for i in range(1, n):Sum.append(a[i] + Sum[i - 1])
# 由于 python 中从 0 开始的下标,所以实现起来会相较于 c++ 来说会有点不同。
ans = 0
for i in range(n - 1):bas = 100000000 - a[i]# 这里相较于 c++ 来说是手写的二分,而不是用 lower_bound 。l, r = i + 1, nwhile l < r:mid = (l + r) // 2if a[mid] >= bas:r = midelse:l = mid + 1ans += Sum[n - 1] - Sum[i] + a[i] * (n - 1 - i)if l < n:ans -= (n - l) * 100000000
print(ans)
D
题目
此题将 f ( x , y ) f(x, y) f(x,y) 转换为纯数学运算可以更好地寻找规律。
我们发现, f ( 11451 , 41919810 ) f(11451, 41919810) f(11451,41919810) 等于这个:
1145141919810 1145141919810 1145141919810
等于
11451 × 100000000 + 41919810 11451 \times 100000000 +41919810 11451×100000000+41919810
所以将其转换成数学运算表达方式等于这个:
f ( x , y ) = x ⋅ 1 0 ⌊ l o g 10 y ⌋ + y f(x,y)=x \cdot 10^{\lfloor log_{10}^y \rfloor} + y f(x,y)=x⋅10⌊log10y⌋+y
仿佛一个一个计算特别麻烦,但是我们可以将数组分成 10 10 10 个集合,每一个集合为 S i S_i Si,对应 A A A 中 i i i 位数的下标。那么可以如此计算:
∑ i = 1 N ∑ j = 1 10 ∑ k ∈ S j f ( A i , A k ) [ k > i ] \sum_{i=1}^N\sum_{j=1}^{10}\sum_{k\in S_j} f(A_i, A_k)[k>i] i=1∑Nj=1∑10k∈Sj∑f(Ai,Ak)[k>i]
此时,第三个循环中 A k A_k Ak 可以用前缀和预处理出来,又由于 A i A_i Ai 的系数固定,可以 O ( 1 ) O(1) O(1) 处理出来。
此时时间复杂度就变成了 O ( 10 n ) O(10n) O(10n),十分优秀。
注意 C++
中取模问题,代码中我使用了后缀和,更方便,但是前缀和与后缀和没什么区别。请再次注意 python
的运行常数,本题我给的 python
代码运行时间 1600ms,c++
代码运行时间 40ms,是 python
的 1 40 \frac1{40} 401
AC Code(CPP)
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, a[200100];
int sum[15][200100], cnt[15][200100];
int p10[20], ans;
int f(int x) {return floor(log10(x)) + 1;
}signed main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n;for (int i = 1; i <= n; i++) {cin >> a[i];}for (int i = n; i >= 1; i--) {for (int j = 1; j <= 11; j++) {sum[j][i] = sum[j][i + 1];cnt[j][i] = cnt[j][i + 1];}sum[f(a[i])][i] += a[i], cnt[f(a[i])][i] += 1;sum[f(a[i])][i] %= 998244353ll;}p10[0] = 1;for (int i = 1; i <= 11; i++) {p10[i] = p10[i - 1] * 10;p10[i] %= 998244353ll;}for (int i = 1; i < n; i++) {for (int j = 1; j <= 11; j++) {ans += (sum[j][i + 1] + (a[i] % 998244353ll) * cnt[j][i + 1] %998244353ll * p10[j] % 998244353ll) % 998244353ll;ans %= 998244353ll;}}cout << ans % 998244353ll;return 0;
}
AC Code(Python)
from math import *n = int(input())
a = [int(i) for i in input().split()]def f(x):return floor(log10(x)) + 1p10 = [1]
for i in range(12):p10.append(p10[i] * 10)
Sum, cnt = [], []
for i in range(n + 2):Sum.append([0] * 12)cnt.append([0] * 12)for i in range(n - 1, -1, -1):for j in range(0, 12):Sum[i][j] = Sum[i + 1][j]cnt[i][j] = cnt[i + 1][j]Sum[i][f(a[i])] += a[i]cnt[i][f(a[i])] += 1ans = 0
for i in range(n - 1):for j in range(1, 12):ans += p10[j] * a[i] * cnt[i + 1][j] + Sum[i + 1][j]ans %= 998244353print(ans)
E
题目
考虑暴力:
先枚举 i , j i,j i,j,再暴力寻找最长前缀,其时间复杂度可以被卡到 O ( N 2 ) O(N^2) O(N2) 以上。
考虑优化:
我们发现,对于每一个字符串,暴力查找时前面共同前缀部分被重复计算了很多次,我们尅确定每一个 i i i,再遍历字符串,再遍历数组,一旦发现有一个串已经枚举过了公共前缀的最后一个字符,加上答案,不管这个字符串了,这样时间复杂度可以被优化一部分,但是最大值仍然是 O ( N ∑ i = 1 n ∣ S i ∣ ) O(N\sum_{i=1}^n|S_i|) O(N∑i=1n∣Si∣) 的,不可取。
我们发现,由于公共前缀都在当前字符串上,所以寻找公共前缀部分可以用字典树(也称 trie 树)实现,对于每一个节点,统计当前位置是这个节点表示的字符的个数(表示为 cnt
),我们遍历字符串,遍历的答案加上该节点的 cnt
,最后返回遍历的答案即可。
由于走到某节点的所有字符串该节点前的前缀都一样,所以字典树是永远正确的。
AC Code(CPP)
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n;
string s[300100];
bool operator <(string a, string b) {return a.size() < b.size();
}
struct node{int p, nxt[30], cnt;
};
node t[4000100];
int cnt = 0;
void add(string s) {int p = 0;for (int i = 0; i < (int)s.size(); i++) {if (!t[p].nxt[s[i] - 'a']) {t[p].nxt[s[i] - 'a'] = ++cnt;}p = t[p].nxt[s[i] - 'a'];t[p].cnt++;}
}
int calc(string s) {int p = 0;int ret = 0;for (int i = 0; i < (int)s.size(); i++) {ret += t[p].cnt;p = t[p].nxt[s[i] - 'a'];}ret += t[p].cnt;return ret;
}
int ans;
signed main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n;for (int i = 1; i <= n; i++) {cin >> s[i];add(s[i]);}for (int i = 1; i <= n; i++) {ans += calc(s[i]) - (int)s[i].size();}cout << ans / 2;return 0;
}
AC Code(Python)
n = int(input())
a = [str(i) for i in input().split()]
cnt, Next = [], []def add_node():cnt.append(0)temp = []for i in range(30):temp.append(0)Next.append(temp)def insert(s):p = 0for i in s:now_char = ord(i) - 97if Next[p][now_char] == 0:add_node()Next[p][now_char] = len(cnt) - 1p = Next[p][now_char]cnt[p] += 1def calc(s):p, ret = 0, 0for i in range(len(s)):now_char = ord(s[i]) - 97p = Next[p][now_char]ret += cnt[p]return ret - len(s)add_node()
for i in range(n):insert(a[i])
ans = 0
for i in range(n):ans += calc(a[i])
print(ans // 2)