ABC245 A~E
- [A - Good morning](https://atcoder.jp/contests/abc245/tasks/abc245_a)
- [B - Mex](https://atcoder.jp/contests/abc245/tasks/abc245_b)
- [C - Choose Elements](https://atcoder.jp/contests/abc245/tasks/abc245_c)
- [D - Polynomial division](https://atcoder.jp/contests/abc245/tasks/abc245_d)
- [E - Wrapping Chocolate](https://atcoder.jp/contests/abc245/tasks/abc245_e)
A - Good morning
题目大意
在同一天里,Takahashi在 A A A时 B B B分起床,Aoki在 C C C时 D D D分 1 1 1秒起床,请问谁起床更早?
0
≤
A
,
C
<
24
0\le A,C<24
0≤A,C<24
0
≤
B
,
D
<
60
0\le B,D<60
0≤B,D<60
输入格式
A B C D A~B~C~D A B C D
输出格式
输出起得更早的人的名字(Takahashi
或Aoki
)。
样例
A A A | B B B | C C C | D D D | 输出 |
---|---|---|---|---|
7 7 7 | 0 0 0 | 6 6 6 | 30 30 30 | Aoki |
7 7 7 | 30 30 30 | 7 7 7 | 30 30 30 | Takahashi |
0 0 0 | 0 0 0 | 23 23 23 | 59 59 59 | Takahashi |
分析
思路很明显,直接判断 ( A , B ) ≤ ( C , D ) (A,B)\le(C,D) (A,B)≤(C,D)是否成立即可。
代码
#include <cstdio>
using namespace std;
int main()
{
int a, b, c, d;
scanf("%d%d%d%d", &a, &b, &c, &d);
puts((a == c? b <= d: a < c)? "Takahashi": "Aoki");
return 0;
}
B - Mex
题目大意
给定整数序列 A = ( A 1 , … , A N ) A=(A_1,\dots,A_N) A=(A1,…,AN)。求最小的不在 A A A中的自然数。
1
≤
N
≤
2000
1\le N\le 2000
1≤N≤2000
0
≤
A
i
≤
2000
0\le A_i\le 2000
0≤Ai≤2000
输入格式
N
N
N
A
1
…
A
N
A_1~\dots~A_N
A1 … AN
输出格式
输出一行,即最小的不在 A A A中的自然数。
样例
略,请自行前往AtCoder查看
分析
由于题面中有限制
0
≤
A
i
≤
2000
0\le A_i\le 2000
0≤Ai≤2000,所以我们直接开一个数组记录
[
0
,
2001
]
[0,2001]
[0,2001]中每个数是否出现过即可。
本题方法很多,这里介绍的是最快的算法,时间复杂度
O
(
N
)
\mathcal O(N)
O(N)。
代码
#include <cstdio>
using namespace std;
bool used[2005];
int main()
{
int n;
scanf("%d", &n);
while(n--)
{
int a;
scanf("%d", &a);
used[a] = true;
}
int i = -1;
while(used[++i]);
printf("%d\n", i);
return 0;
}
C - Choose Elements
题目大意
给定两个长度为
N
N
N的整数序列
A
=
(
A
1
,
…
,
A
N
)
A=(A_1,\dots,A_N)
A=(A1,…,AN)和
B
=
(
B
1
,
…
,
B
N
)
B=(B_1,\dots,B_N)
B=(B1,…,BN)。
问是否存在序列
X
=
(
X
1
,
…
,
X
N
)
X=(X_1,\dots,X_N)
X=(X1,…,XN),满足如下条件:
- X i = A i X_i=A_i Xi=Ai或 X i = B i X_i=B_i Xi=Bi
- ∣ X i − X i + 1 ∣ ≤ K |X_i-X_{i+1}|\le K ∣Xi−Xi+1∣≤K,其中 1 ≤ i < N 1\le i<N 1≤i<N
1
≤
N
≤
2
×
1
0
5
1\le N\le 2\times 10^5
1≤N≤2×105
1
≤
K
≤
1
0
9
1\le K\le 10^9
1≤K≤109
1
≤
A
i
,
B
i
≤
1
0
9
1\le A_i,B_i\le 10^9
1≤Ai,Bi≤109
输入格式
N
K
N~K
N K
A
1
…
A
N
A_1~\dots~A_N
A1 … AN
B
1
…
B
N
B_1~\dots~B_N
B1 … BN
输出格式
如果存在符合全部条件的
X
X
X,输出Yes
;否则,输出No
。
样例
略,请自行前往AtCoder查看
分析
好家伙,C题都要用dp……
本题普通的方法貌似不太好做,因此我们考虑
DP
\text{DP}
DP。
令
f
(
i
)
=
X
i
f(i)=X_i
f(i)=Xi选择能否等于
A
i
A_i
Ai,
g
(
i
)
=
X
i
g(i)=X_i
g(i)=Xi能否等于
B
i
B_i
Bi。
然后状态转移方程就简单了,详见代码。
代码
#include <cstdio>
#define maxn 200005
using namespace std;
int a[maxn], b[maxn];
bool f[maxn], g[maxn];
int main()
{
int n, k;
scanf("%d%d", &n, &k);
for(int i=0; i<n; i++)
scanf("%d", a + i);
for(int i=0; i<n; i++)
scanf("%d", b + i);
f[0] = g[0] = true;
#define set(x, y, z) x |= y - z <= k && z - y <= k
for(int i=1; i<n; i++)
{
if(f[i - 1])
set(f[i], a[i - 1], a[i]),
set(g[i], a[i - 1], b[i]);
if(g[i - 1])
set(f[i], b[i - 1], a[i]),
set(g[i], b[i - 1], b[i]);
}
puts(f[n - 1] || g[n - 1]? "Yes": "No");
return 0;
}
注:本题还有一种很奇怪的解法,就是直接判断相邻的四种连接方式是否有至少一种能连通,比如#30453703,如果有大佬能证明这种方法的正确性,欢迎在评论区留言告诉我,谢谢!
D - Polynomial division
题目大意
我们有三个多项式
A
(
x
)
=
∑
i
=
0
N
A
i
X
i
B
(
x
)
=
∑
i
=
0
M
B
i
X
i
C
(
x
)
=
∑
i
=
0
N
+
M
B
i
X
i
A(x)=\sum_{i=0}^N A_iX^i\\ B(x)=\sum_{i=0}^M B_iX^i\\ C(x)=\sum_{i=0}^{N+M} B_iX^i
A(x)=i=0∑NAiXiB(x)=i=0∑MBiXiC(x)=i=0∑N+MBiXi
已知
A
0
,
…
,
A
N
A_0,\dots,A_N
A0,…,AN和
C
0
,
…
,
C
N
C_0,\dots,C_N
C0,…,CN且
A
(
x
)
×
B
(
x
)
=
C
(
x
)
A(x)\times B(x)=C(x)
A(x)×B(x)=C(x)(
x
∈
R
x\in R
x∈R),求
B
0
,
…
,
B
M
B_0,\dots,B_M
B0,…,BM。
换句话说,给定多项式
A
A
A和
C
C
C每一项的系数,求多项式
B
=
C
A
B=\frac C A
B=AC。
1
≤
N
,
M
<
100
1\le N,M<100
1≤N,M<100
∣
A
i
∣
≤
100
|A_i|\le 100
∣Ai∣≤100
∣
C
i
∣
≤
1
0
6
|C_i|\le 10^6
∣Ci∣≤106
A
N
≠
0
,
C
N
+
M
≠
0
A_N\ne0,C_{N+M}\ne0
AN=0,CN+M=0
题目保证存在合法的
(
B
0
,
…
,
B
M
)
(B_0,\dots,B_M)
(B0,…,BM)。
输入格式
N
M
N~M
N M
A
0
…
A
N
A_0~\dots~A_N
A0 … AN
C
0
…
C
N
+
M
C_0~\dots~C_{N+M}
C0 … CN+M
输出格式
输出 B 0 , … , B M B_0,\dots,B_M B0,…,BM,用空格分隔。
样例
略,请自行前往AtCoder查看
分析
本题可以直接模拟多项式的大除法运算,运算时只需记录系数即可。
代码
#include <cstdio>
#define maxn 105
using namespace std;
int a[maxn], b[maxn], c[maxn << 1];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for(int i=0; i<=n; i++) scanf("%d", a + i);
for(int i=0; i<=n+m; i++) scanf("%d", c + i);
for(int i=m; i>=0; i--) // NOTE: 必须倒推!
{
b[i] = c[n + i] / a[n];
for(int j=0; j<=n; j++)
c[i + j] -= a[j] * b[i];
}
for(int i=0; i<=m; i++)
printf("%d ", b[i]);
return 0;
}
E - Wrapping Chocolate
题目大意
我们有
N
N
N块巧克力和
M
M
M个盒子。第
i
i
i块巧克力长
A
i
A_i
Ai厘米,宽
B
i
B_i
Bi厘米;第
i
i
i个盒子长
C
i
C_i
Ci厘米,宽
D
i
D_i
Di厘米。
问是否能把巧克力分别装在盒子里,使其满足如下条件:
- 每个盒子里只能有一块巧克力。
- 当我们将第 i i i块巧克力放入第 j j j个盒子里时, A i ≤ C j A_i\le C_j Ai≤Cj和 B i ≤ D j B_i\le D_j Bi≤Dj必须都成立。
1
≤
N
≤
M
≤
2
×
1
0
5
1\le N\le M\le 2\times10^5
1≤N≤M≤2×105
1
≤
A
i
,
B
i
,
C
i
,
D
i
≤
1
0
9
1\le A_i,B_i,C_i,D_i\le 10^9
1≤Ai,Bi,Ci,Di≤109
输入格式
N
M
N~M
N M
A
1
…
A
N
A_1~\dots~A_N
A1 … AN
B
1
…
B
N
B_1~\dots~B_N
B1 … BN
C
1
…
C
N
C_1~\dots~C_N
C1 … CN
D
1
…
D
N
D_1~\dots~D_N
D1 … DN
输出格式
如果有合法的方法,输出Yes
;否则,输出No
。
分析
本题可以考虑如下贪心算法:
- 将所有的巧克力和盒子放入一个数组,按长度( A i A_i Ai或 C i C_i Ci)的降序排序,长度相等的把盒子排在前面。
- 准备好一个空序列
S
=
(
)
S=()
S=(),按如下规则遍历每个元素:
- 如果当前遍历的是一个盒子
(
C
i
,
D
i
)
(C_i,D_i)
(Ci,Di):
将 D i D_i Di加入 S S S。 - 如果当前遍历的是一块巧克力
(
A
i
,
B
i
)
(A_i,B_i)
(Ai,Bi):
从 S S S中删除不超过 B i B_i Bi的最小元素,如果没有元素可删除,输出No
。
- 如果当前遍历的是一个盒子
(
C
i
,
D
i
)
(C_i,D_i)
(Ci,Di):
- 如果顺利地遍历了所有元素,输出
Yes
;否则,输出No
。
本算法的时间复杂度是
O
(
M
N
)
\mathcal O(MN)
O(MN),但经过multiset
优化后可降为
O
(
(
M
+
N
)
log
(
M
+
N
)
\mathcal O((M+N)\log(M+N)
O((M+N)log(M+N),具体实现详见代码。
代码
#include <cstdio>
#include <set>
#include <algorithm>
using namespace std;
struct Item {
int w, h;
bool type;
inline bool operator <(const Item& i2) const {
return w == i2.w? type > i2.type: w > i2.w;
// ^^^^^^^^^^^^^^
// 注意sort必须有严格顺序,一开始我这里写成了type==1导致RE,详见:
// https://atcoder.jp/contests/abc245/submissions/30526563
}
} v[400005];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
// Chocolate
for(int i=0; i<n; i++) scanf("%d", &v[i].w);
for(int i=0; i<n; i++) scanf("%d", &v[i].h);
// Box
m += n;
for(int i=n; i<m; i++) scanf("%d", &v[i].w);
for(int i=n; i<m; i++) scanf("%d", &v[i].h);
for(int i=n; i<m; i++) v[i].type = 1;
// Algorithm
sort(v, v + m);
multiset<int> s;
for(int i=0; i<m; i++)
{
const Item& it = v[i];
if(it.type) s.insert(it.h); // Box
else
{
auto itr = s.lower_bound(it.h);
if(itr == s.end())
{
puts("No");
return 0;
}
s.erase(itr);
}
}
puts("Yes");
return 0;
}