• ABC353 A-E题解



    下面的内容不包括题目翻译,要想获取题目翻译,请参照 这篇教程 来获取题目翻译。

    A

    题目

    从左到右遍历数组,找到第一个比数组第一个元素大的元素,输出坐标即可。如果没有输出 -1 即可。

    AC Code(CPP)

    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    AC Code(Python)

    n = int(input())
    a = [int(i) for i in input().split()]
    flag = 0
    
    for i in range(len(a)):
    	if a[i] > a[0]:
    		print(i + 1)
    		flag = 1
    		break
    print(("-1", "")[flag])
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    B

    题目

    就是个模拟,按照题目所给信息编码即可。

    AC Code(CPP)

    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    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 = 0
    		cnt += 1
    	Sum += i
    print(cnt + 1)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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 Ai108,所以 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+Aj108(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 
    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    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, n
    	while l < r:
    		mid = (l + r) // 2
    		if a[mid] >= bas:
    			r = mid
    		else:
    			l = mid + 1
    	ans += Sum[n - 1] - Sum[i] + a[i] * (n - 1 - i)
    	if l < n:
    		ans -= (n - l) * 100000000
    print(ans)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    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)=x10log10y+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=1Nj=110kSjf(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 
    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    AC Code(Python)

    from math import *
    
    n = int(input())
    a = [int(i) for i in input().split()]
    
    
    def f(x):
    	return floor(log10(x)) + 1
    
    
    p10 = [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])] += 1
    
    ans = 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 %= 998244353
    
    
    print(ans)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    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(Ni=1nSi) 的,不可取。

    我们发现,由于公共前缀都在当前字符串上,所以寻找公共前缀部分可以用字典树(也称 trie 树)实现,对于每一个节点,统计当前位置是这个节点表示的字符的个数(表示为 cnt),我们遍历字符串,遍历的答案加上该节点的 cnt,最后返回遍历的答案即可。

    由于走到某节点的所有字符串该节点前的前缀都一样,所以字典树是永远正确的。

    AC Code(CPP)

    #include 
    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    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 = 0
    	for i in s:
    		now_char = ord(i) - 97
    		if Next[p][now_char] == 0:
    			add_node()
    			Next[p][now_char] = len(cnt) - 1
    		p = Next[p][now_char]
    		cnt[p] += 1
    
    
    def calc(s):
    	p, ret = 0, 0
    	for i in range(len(s)):
    		now_char = ord(s[i]) - 97
    		p = 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)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
  • 相关阅读:
    《程序员职场工具库》如何优化你的工作 —— PDCA 循环
    C++对象模型探索--04数据语义
    React - 受控组件与非受控组件(实践妙用-模糊查询)
    vite+rollup
    深度学习目标检测——AP以及MAP
    传统企业如何实现数字化转型?这套方法论超实用!
    关于Spring的两三事:万物之始—BeanDefinition
    Behave介绍和快速示例
    WFST--学习笔记
    inno setup自定义卸载程序和美化
  • 原文地址:https://blog.csdn.net/m0_72961775/article/details/138769393