ABC200/KYOCERA2021 A~E
- [A - Century](https://atcoder.jp/contests/abc200/tasks/abc200_a)
- 题目大意
- 输入格式
- 输出格式
- 样例
- 分析
- 代码
- [B - 200th ABC-200](https://atcoder.jp/contests/abc200/tasks/abc200_b)
- 题目大意
- 输入格式
- 输出格式
- 样例
- 分析
- 代码
- [C - Ringo's Favorite Numbers 2](https://atcoder.jp/contests/abc200/tasks/abc200_c)
- 题目大意
- 输出格式
- 样例
- 分析
- 代码
- [D - Happy Birthday! 2](https://atcoder.jp/contests/abc200/tasks/abc200_d)
- 题目大意
- 输入格式
- 输出
- 样例
- 分析
- 代码
- [E - Patisserie ABC 2](https://atcoder.jp/contests/abc200/tasks/abc200_e)
- 题目大意
- 输入格式
- 输出格式
- 样例
- 分析
- 代码
A - Century
题目大意
公元 N N N年在第几个世纪中?
一个世纪是由 100 100 100个年份组成的一个区间。如, 1 1 1世纪为 [ 1 , 100 ] [1,100] [1,100]年, 2 2 2世纪为 [ 101 , 200 ] [101,200] [101,200]年,……
1 ≤ N ≤ 3000 1\le N\le 3000 1≤N≤3000
输入格式
N N N
输出格式
将答案输出为一个整数。
样例
N N N | 输出 |
---|---|
2021 2021 2021 | 21 21 21 |
200 200 200 | 2 2 2 |
分析
根据本题条件我们可以推出:公元 N N N年在世纪 ⌈ N 100 ⌉ \lceil \frac N {100}\rceil ⌈100N⌉。
代码
#include <cstdio>
using namespace std;int main()
{int n;scanf("%d", &n);printf("%d\n", n % 100 == 0? n / 100: n / 100 + 1);return 0;
}
B - 200th ABC-200
题目大意
对于整数 N N N,执行 K K K次如下操作:
- 如果 N N N是 200 200 200的倍数,将 N N N除以 200 200 200。
- 否则,在 N N N后面添上 200 200 200。(如, 123 123 123变为 123200 123200 123200)。
1 ≤ N ≤ 1 0 5 1\le N\le 10^5 1≤N≤105
1 ≤ K ≤ 20 1\le K\le 20 1≤K≤20
输入格式
N K N~K N K
输出格式
输出最终的 N N N。
样例
N N N | K K K | 输出 |
---|---|---|
2021 2021 2021 | 4 4 4 | 50531 50531 50531 |
40000 40000 40000 | 2 2 2 | 1 1 1 |
8691 8691 8691 | 20 20 20 | 84875488281 84875488281 84875488281 |
分析
本题我们按照题意模拟即可,但我们需要证明答案不会超过 2 63 − 1 2^{63}-1 263−1,这样才能使用long long
:
- 任何数 N N N添上 200 200 200都是 200 200 200的倍数。证明:一个数只要是 100 100 100和 2 2 2的公倍数,它就是 200 200 200的倍数。 N N N添上 200 200 200后以
00
结尾,就证明了它是 200 200 200的倍数。 - 这样, N N N每次添上 200 200 200后都要除以 200 200 200,相当于去掉两个零再将 N N N除以 2 2 2。
- 所以, N N N每次最多增加一位,还经常减少位数(除以 200 200 200),肯定严格小于 2 63 2^{63} 263。
代码
#include <cstdio>
using namespace std;int main()
{long long n;int k;scanf("%lld%d", &n, &k);while(k--)n = n % 200LL == 0LL? n / 200LL: n * 1000LL + 200LL;printf("%lld\n", n);return 0;
}
C - Ringo’s Favorite Numbers 2
题目大意
给定序列 A = ( A 1 , A 2 , … , A N ) A=(A_1,A_2,\dots,A_N) A=(A1,A2,…,AN),找到符合下列条件的所有 ( i , j ) (i,j) (i,j):
- 1 ≤ i < j ≤ N 1\le i < j\le N 1≤i<j≤N;
- ∣ A i − A j ∣ |A_i-A_j| ∣Ai−Aj∣是 200 200 200的倍数。
2 ≤ N ≤ 2 × 1 0 5 2\le N\le 2\times 10^5 2≤N≤2×105
1 ≤ A i ≤ 1 0 9 1\le A_i\le 10^9 1≤Ai≤109
输出格式
N N N
A 1 A 2 … A N A_1~A_2~\dots~A_N A1 A2 … AN
样例
A A A | 输出 |
---|---|
( 123 , 223 , 123 , 523 , 200 , 2000 ) (123,223,123,523,200,2000) (123,223,123,523,200,2000) | 4 4 4 |
( 1 , 2 , 3 , 4 , 5 ) (1,2,3,4,5) (1,2,3,4,5) | 0 0 0 |
( 199 , 100 , 200 , 400 , 300 , 500 , 600 , 200 ) (199,100,200,400,300,500,600,200) (199,100,200,400,300,500,600,200) | 9 9 9 |
分析
首先,最容易想到的 O ( n 2 ) \mathcal O(n^2) O(n2)的暴力算法肯定不行,因为 2 ≤ N ≤ 2 × 1 0 5 2\le N\le 2\times 10^5 2≤N≤2×105。
我们考虑用桶优化:
我们有 200 200 200个桶,分别如下:
桶的编号 | 元素 1 1 1 | 元素 2 2 2 | 元素 3 3 3 | 元素 4 4 4 | …… | 元素除以 200 200 200的余数 |
---|---|---|---|---|---|---|
0 0 0 | 0 0 0 | 200 200 200 | 400 400 400 | 600 600 600 | …… | 0 0 0 |
1 1 1 | 1 1 1 | 201 201 201 | 401 401 401 | 601 601 601 | …… | 1 1 1 |
2 2 2 | 2 2 2 | 202 202 202 | 402 402 402 | 602 602 602 | …… | 2 2 2 |
…… | …… | …… | …… | …… | …… | …… |
198 198 198 | 198 198 198 | 398 398 398 | 598 598 598 | 798 798 798 | …… | 198 198 198 |
199 199 199 | 199 199 199 | 399 399 399 | 599 599 599 | 799 799 799 | …… | 199 199 199 |
这时,我们发现,每个桶中的每个元素都能与这个同种的其他元素组成一对,所以我们只要在将元素加入桶中前将答案加上桶目前的大小即可。 |
总时间复杂度 O ( n ) \mathcal O(n) O(n)。
代码
我们的桶中不需要真正的元素,只需要记录桶的大小即可。
注意:答案的数据类型一定要用long long
!
#include <cstdio>
#define MOD 200
using namespace std;int cnt[MOD];int main()
{int n;scanf("%d", &n);long long ans = 0LL;while(n--){int x;scanf("%d", &x);ans += cnt[x %= MOD] ++;}printf("%lld\n", ans);return 0;
}
D - Happy Birthday! 2
题目大意
给定序列 A = ( A 1 , A 2 , … , A N ) A=(A_1,A_2,\dots,A_N) A=(A1,A2,…,AN)。你要从中选出两个子序列 B B B和 C C C(下标不同,不要求连续),使得 ∑ B ≡ ∑ C ( m o d 200 ) \sum B\equiv \sum C\pmod{200} ∑B≡∑C(mod200)。
2 ≤ N ≤ 200 2\le N\le 200 2≤N≤200
1 ≤ A i ≤ 1 0 9 1\le A_i\le 10^9 1≤Ai≤109
输入格式
N N N
A 1 A 2 … A N A_1~A_2~\dots~A_N A1 A2 … AN
输出
如果没有合法的 B B B和 C C C,输出No
。
如果有合法的 B B B和 C C C,按下列格式输出,其中 x x x为 B B B的长度、 y y y为 C C C的长度, B ′ B' B′为 B B B中元素对应的下标, C ′ C' C′为 C C C中元素对应的下标:
Yes \text{Yes} Yes
x B 1 ′ B 2 ′ … B x ′ x~B'_1~B'_2~\dots~B'_x x B1′ B2′ … Bx′
y C 1 ′ C 2 ′ … C y ′ y~C'_1~C'_2~\dots~C'_y y C1′ C2′ … Cy′
样例
略,请自行前往AtCoder查看
分析
我们可以证明,只要 N ≥ 8 N\ge 8 N≥8,那么就一定有解。证明如下:
- 8 8 8个元素能组成的子序列有 2 8 − 1 = 255 2^8-1=255 28−1=255种。(每个元素可以选或不选,去掉全不选的情况)
- 根据抽屉原理,我们将这 255 255 255种子序列按照他们除以 200 200 200的余数分别放入抽屉中,则至少有两个子序列在一个抽屉中,即必定有合法的 A A A和 B B B。
当 N < 8 N<8 N<8时,我们暴力枚举所有可能;
当 N ≥ 8 N\ge8 N≥8时,我们暴力枚举其中任意 8 8 8个元素组成的所有可能即可找到解。
代码
#include <cstdio>
#include <vector>
#define maxn 10
#define MOD 200
using namespace std;int a[maxn];
vector<int> bkt[MOD];inline void print(const vector<int>& v)
{printf("%llu", v.size());for(int x: v)printf(" %d", x + 1);putchar('\n');
}int main()
{int n;scanf("%d", &n);if(n > 8) n = 8;for(int i=0; i<n; i++)scanf("%d", a + i);int lim = 1 << n;for(int st=0; st<lim; st++){int s = 0;vector<int> pos;for(int i=0; i<n; i++)if(st >> i & 1)s = (s + a[i]) % MOD, pos.push_back(i);if(!bkt[s].empty()){puts("Yes");print(bkt[s]);print(pos);return 0;}else bkt[s] = pos;}puts("No");return 0;
}
E - Patisserie ABC 2
题目大意
有 N 3 N^3 N3个三元组 ( i , j , k ) (i,j,k) (i,j,k)( 1 ≤ i , j , k ≤ N 1\le i,j,k\le N 1≤i,j,k≤N),按如下排序:
- i + j + k i+j+k i+j+k小的排在前面。
- 对于 i + j + k i+j+k i+j+k相同的三元组, i i i小的排在前面,对于 i i i相同的, j j j小的排在前面。
求第 K K K个 ( i , j , k ) (i,j,k) (i,j,k)。
1 ≤ N ≤ 1 0 6 1\le N\le 10^6 1≤N≤106
1 ≤ K ≤ N 3 1\le K\le N^3 1≤K≤N3
输入格式
N K N~K N K
输出格式
输出第 K K K个 ( i , j , k ) (i,j,k) (i,j,k),用空格分隔。
样例
N N N | K K K | 输出 |
---|---|---|
2 2 2 | 5 5 5 | 1 2 2 1~2~2 1 2 2 |
1000000 1000000 1000000 | 1000000000000000000 1000000000000000000 1000000000000000000 | 1000000 1000000 1000000 1000000~1000000~1000000 1000000 1000000 1000000 |
9 9 9 | 47 47 47 | 3 1 4 3~1~4 3 1 4 |
分析
由于每个三元组的和都不会超过 3 N 3N 3N,所以我们考虑暴力枚举三元组的和,这样就能快速算出第 K K K个三元组的和。那么,问题来了:符合 i + j + k = n i+j+k=n i+j+k=n的三元组 ( i , j , k ) (i,j,k) (i,j,k)( i , j , k i,j,k i,j,k均不超过 N N N)有多少个?
这可以用容斥原理解决。首先,我们发现,上述问题实际上就是在求如下方程解的个数:
{ i + j + k = n 1 ≤ i , j , k ≤ N \begin{cases}i+j+k=n\\1\le i,j,k\le N\end{cases} {i+j+k=n1≤i,j,k≤N
如果我们将问题改成求如下方程解的个数(注意 n n n和 N N N的区别):
{ i + j + k = n 1 ≤ i , j , k ≤ n \begin{cases}i+j+k=n\\1\le i,j,k\le n\end{cases} {i+j+k=n1≤i,j,k≤n
这个可以用挡板法解决,在 n − 1 n-1 n−1个位置上任选 2 2 2个插入挡板,挡板分开的就是 i , j , k i,j,k i,j,k,则公式为 C n − 1 2 C_{n-1}^2 Cn−12。我们设 f ( n ) = f(n)=~ f(n)= 上述方程解的个数( C n − 1 2 = ( n − 1 ) ( n − 2 ) C_{n-1}^2=(n-1)(n-2) Cn−12=(n−1)(n−2)),则总方程解的个数为 f ( n ) f(n) f(n)。
我们考虑一个、两个(不可能有三个)数大于 N N N的情况,这样 { i + j + k = n 1 ≤ i , j , k ≤ N \begin{cases}i+j+k=n\\1\le i,j,k\le N\end{cases} {i+j+k=n1≤i,j,k≤N这个方程解的个数就为 f ( n ) + 3 f ( n − 2 N ) − 3 f ( n − N ) f(n)+3f(n-2N)-3f(n-N) f(n)+3f(n−2N)−3f(n−N)。
代码
计算 f ( n ) f(n) f(n)时注意特判 n ≤ 2 n\le 2 n≤2的情况,否则可能会出现负数!
#include <cstdio>
using namespace std;using LL = long long;
int n;inline int max(int x, int y) { return x > y? x: y; }
inline int min(int x, int y) { return x < y? x: y; }inline LL f(LL n) { return n-- > 2? n * (n - 1LL) >> 1LL: 0LL; }
inline LL count(int s) { return f(s) - 3 * (f(s - n) - f(s - (n << 1))); }int main()
{LL k;scanf("%d%lld", &n, &k);int lim = n * 3;for(int sum=3; sum<=lim; sum++){LL cnt = count(sum);if(k > cnt) { k -= cnt; continue; }for(int a=1; a<=n; a++){int minb = max(1, sum - a - n), maxb = min(n, sum - a - 1);if(minb > maxb) continue;int num = maxb - minb + 1;if(k <= num){int b = minb + k - 1;int c = sum - a - b;printf("%d %d %d\n", a, b, c);return 0;}k -= num;}}return 0;
}