ABC353 A-E题解

14 篇文章 0 订阅


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

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 = 0

for i in range(len(a)):
	if a[i] > a[0]:
		print(i + 1)
		flag = 1
		break
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 = 0
		cnt += 1
	Sum += 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 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 <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, 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)

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 <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)) + 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)

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 <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 = 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)

  • 22
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值