传送门
Codeforces Round #681 (Div. 2, based on VK Cup 2019-2020 - Final)
A
从 [ 1 , 4 n ] [1,4n] [1,4n] 选择 n n n 个数字,使任意数对 ( x , y ) (x,y) (x,y) 都不满足 g c d ( x , y ) = 1 gcd(x,y)=1 gcd(x,y)=1;以及 x ∣ y x|y x∣y 或 y ∣ x y|x y∣x。对于第一个限制,可以赋所有数字一个大于 1 1 1 的公约数,显然赋 2 2 2 可以在规定区间内获得更多满足要求的数字。对于第二个条件,使所有数字除了公约数,另一个因子不满足可相互整除,那么选择 [ n + 1 , 2 n ] [n+1,2n] [n+1,2n] 即可满足条件。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
int main()
{
int t, n;
scanf("%d", &t);
while (t--)
{
scanf("%d", &n);
for (int i = n + 1; i <= (n << 1); ++i)
{
printf("%d%c", i << 1, i == (n << 1) ? '\n' : ' ');
}
}
return 0;
}
B
若只有一个连续 ′ 1 ′ '1' ′1′ 的区域,答案为 a a a;若有多个这样的区域,从左往右贪心地考虑第二个及之后的区域,若直接引爆,花费为 a a a,若选择和左一个区域一起引爆,设两个区域间有 n n n 个 ′ 0 ′ '0' ′0′,花费为 n × b n\times b n×b。若 n × b > a n\times b > a n×b>a 则单独引爆;反之,选择连接左一个区域后一起引爆。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 100005
char mp[maxn];
int main()
{
int t, a, b;
scanf("%d", &t);
while (t--)
{
scanf("%d%d", &a, &b);
scanf(" %s", mp);
bool f = 0;
int n = strlen(mp), cnt = 0, res = 0;
for (int i = 0; i < n; ++i)
{
f |= mp[i] == '1';
if (mp[i] == '0')
{
if (f)
++cnt;
}
else if (cnt > 0)
{
res += cnt * b < a ? cnt * b : a;
cnt = 0;
}
}
printf("%d\n", res + (f ? a : 0));
}
return 0;
}
C
设两个集合 S , T S,T S,T 分别代表选择快递员配送与自己去取两种选择的餐厅,那么答案为 m a x { a i } + s u m { b j } , a i ∈ S , b j ∈ T max\{a_i\}+sum\{b_j\},a_i\in S,b_j\in T max{ai}+sum{bj},ai∈S,bj∈T。对于确定的 m a x { a i } max\{a_i\} max{ai} 显然最优的做法是将满足 a i ′ ≤ m a x { a i } a_{i'}\leq max\{a_i\} ai′≤max{ai} 的餐厅全部加入 S S S,使 s u m { b j } sum\{b_j\} sum{bj} 尽可能小。那么将餐厅按照 a i a_i ai 排序,枚举 m a x { a i } max\{a_i\} max{ai},用前缀和维护 s u m { b j } sum\{b_j\} sum{bj},就可以 O ( n l o g n + n ) O(nlogn+n) O(nlogn+n) 求解最小花费时间。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 200005
typedef long long ll;
typedef pair<ll, ll> P;
ll sum[maxn];
P ps[maxn];
int main()
{
int t, n;
scanf("%d", &t);
while (t--)
{
scanf("%d", &n);
for (int i = 0; i < n; ++i)
scanf("%lld", &ps[i].first);
for (int i = 0; i < n; ++i)
scanf("%lld", &ps[i].second);
sort(ps, ps + n);
for (int i = 0; i < n; ++i)
sum[i + 1] = sum[i] + ps[i].second;
ll res = sum[n];
for (int i = 0; i < n; ++i)
{
ll t = max(ps[i].first, sum[n] - sum[i + 1]);
res = min(res, t);
}
printf("%lld\n", res);
}
return 0;
}
D 补题
数组应该由两个数组相加构成:一个非严格递增数组,即
[
k
,
n
)
[k,n)
[k,n) 的减一操作,和一个非严格递减数组,即
[
0
,
k
)
[0,k)
[0,k) 的减一操作。从左向右扫描,为了满足非严格递增数组,那么非严格递减数组应该尽可能的大。设
l
e
f
t
,
r
i
g
h
t
left,right
left,right 分别代表某个位置左侧非严格递减数组能提供的最大值,与非严格递增数组可能提供的最小值。那么有
{
r
i
g
h
t
=
m
a
x
(
A
i
−
l
e
f
t
,
0
)
,
l
e
f
t
=
m
i
n
(
l
e
f
t
,
A
[
i
]
)
r
i
g
h
t
≤
A
[
i
]
−
l
e
f
t
l
e
f
t
=
A
[
i
]
−
r
i
g
h
t
A
[
i
]
≥
r
i
g
h
t
>
A
[
i
]
−
l
e
f
t
i
m
p
o
s
s
i
l
b
l
e
o
t
h
e
r
w
i
s
e
\begin{cases} right = max(A_i-left,0),left = min(left,A[i]) & right\leq A[i]-left\\ left=A[i]-right & A[i]\geq right > A[i]-left \\ impossilble & otherwise \\ \end{cases}
⎩⎪⎨⎪⎧right=max(Ai−left,0),left=min(left,A[i])left=A[i]−rightimpossilbleright≤A[i]−leftA[i]≥right>A[i]−leftotherwise
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 30005
int A[maxn];
int main()
{
int t, n;
scanf("%d", &t);
while (t--)
{
scanf("%d", &n);
for (int i = 0; i < n; ++i)
scanf("%d", A + i);
bool f = 1;
int left = A[0], right = -1e9;
for (int i = 1; i < n; ++i)
{
if (right > A[i] - left)
{
if (right > A[i])
{
f = 0;
break;
}
else
left = A[i] - right;
}
else
{
right = max(A[i] - left, 0);
left = min(left, A[i]);
}
}
puts(f ? "YES" : "NO");
}
return 0;
}