AtCoder Regular Contest 126(补题)
Posted 佐鼬Jun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AtCoder Regular Contest 126(补题)相关的知识,希望对你有一定的参考价值。
A - Make 10
题目链接: link.
题意:
给A个长度为2的木棍,B个长度为3的木棍,C个长度为4的木棍,通过拼接木棍,最多能拼成多少个长度为10的木棍
思路:
由于长度为3的木棍要与其他木棍拼接成10的木棍(3322,334),长度3木棍必须是偶数,所以就把长度为3的木棍的数量就当成⌊
B
2
\\frac{B}{2}
2B⌋,记为D,如果D>0,且其他两种长度木棍数量也够和D组合,那么最优解里一定存在数字6和4的搭配(4可以是两个2),用反证法,如果最优解里面
不存在6和4的搭配,那么最优解里面一定是一堆2和4的搭配,那么拿出一个4或者两个2,得到的木棍数量一定大于等于(>=)最优解,这与最优解矛盾,所以一定要有6和4的搭配。由于数字2比数字4更具有灵活性,即数字2还可以单独形成数字10,而数字4不可以,所以优先用数字4再用2,所以用木棍优先级顺序就出来了,334,3322,442,4222,22222,这五种搭配,按照上述顺序来用就是最优解。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int main() {
int T;
cin >> T;
while (T--) {
ll A, B, C; // 2,3,4
cin >> A >> B >> C;
B /= 2;
ll res = 0;
ll x = min(B, C);
res += x;
B -= x, C -= x;
x = min(A / 2, B);
res += x;
B -= x;
A -= 2 * x;
x = min(A, C / 2);
res += x;
C -= 2 * x;
A -= x;
x = min(C, A / 3);
res += x;
A -= 3 * x;
C -= x;
cout << res + A / 5 << endl;
}
}
B - Cross-free Matching
链接: link
题意:
现在一共有 2 N 2N 2N个点, x x x在 [ 1 , N ] [1,N] [1,N]区间, y y y是0或1,现在输入 m m m行,每行2个数字a,b,表示(a,0)点,(b,1)点之间有连线,现在从这 m m m个连线中,选出 k k k个,保证连线不相交(有交点就算相交),输出最大k
思路:
先按照 a a a从小到大, b b b从大到小的优先级来排序,然后用树状数组来存当前 b b b前面最多连接了多少连线,由于提前排过序了,所以当前取出来的坐标的a值一定大于等于前面的坐标的a值,即 a n o w a_{now} anow > = >= >= a p r e a_{pre} apre,又由于树状数组每次取出来的都是当前b值之前的值,所以在前面连好了最多的连线的情况下,现在在连接一条连线一定是不冲突的。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
typedef pair<int, int> PII;
PII a[N];
int tr[N];
int n, m;
bool cmp(PII a, PII b) {
if (a.first == b.first) return a.second > b.second;
return a.first < b.first;
}
int lowbit(int x) { return x & -x; }
void modify(int now, int x) {
for (int i = now; i <= n; i += lowbit(i)) {
tr[i] = max(tr[i], x);
}
}
int query(int now) {
int res = 0;
for (int i = now; i; i -= lowbit(i)) {
res = max(res, tr[i]);
}
return res;
}
int main() {
cin >> n >> m;
for (int i = 0; i < m; i++) {
cin >> a[i].first >> a[i].second;
}
sort(a, a + m, cmp);
int res = 0;
for (int i = 0; i < m; i++) {
int x = a[i].second;
int cnt = query(x - 1) + 1;
res = max(res, cnt);
modify(x, cnt);
}
cout << res << endl;
return 0;
}
C - Maximize GCD
链接: link.
题意:
给 n n n个数, a 1 , a 2 . . . . a n a_1,a_2....a_n a1,a2....an,并且可以操作 k k k次,每次操作可以选择使得一个数 a i a_i ai加上 1 1 1,问在操作最多 k k k次情况下,整个数组的最大公约数最大是多少。
思路:
如果选定的最大公约数是
x
x
x,那么所有数,一定都
(
k
−
1
)
x
<
A
i
<
=
k
x
(k-1)x<A_i<=kx
(k−1)x<Ai<=kx,要通过加数使得
A
i
A_i
Ai变成
k
x
kx
kx,单独的一个数就是
k
x
−
A
i
kx-A_i
kx−Ai,对于整个数组来说,就是
∑
i
=
1
n
(
k
x
−
A
i
)
\\sum \\limits_{i=1} ^{n}(kx-Ai)
i=1∑n(kx−Ai),(注:对于每个数
A
i
A_i
Ai,k是不一定相同的), 把式子展开就变成了
∑
i
=
1
n
k
x
\\sum \\limits_{i=1} ^{n}kx
i=1∑nkx
−
-
−
∑
i
=
1
n
A
i
\\sum \\limits_{i=1} ^{n}A_i
i=1∑nAi,,此时通过式子可发现,只需要提前处理出来
A
i
A_i
Ai的前缀和,再枚举
x
x
x,对于
x
x
x,确定出不同的
k
k
k出来,再看对于的
(
(
k
−
1
)
x
,
k
x
]
((k-1)x,kx]
((k−1)x,kx](左开有闭)这个区间的前缀和即可。
最后还要注意的一点是,通过数据范围可以发现,
k
k
k数据范围远大于
A
i
A_i
Ai,所以可能出现,不停地加数,把A数组中的所有数,都加到非常大,写成公式形式就是,
c
o
s
t
=
N
∗
n
−
p
r
e
s
u
m
[
N
]
cost=N*n-presum[N]
cost=N∗n−presum[N],把所有数变成上限N的操作数,如果
c
o
s
t
<
=
k
cost<=k
cost<=k,那么最终的数就是
a
n
s
=
N
+
(
k
−
c
o
s
t
)
/
n
ans=N+(k-cost)/n
ans=N+(k−cost)/n
具体的代码操作,先存储每个数出现的次数,且每个数的和,再提前预处理出来其每个数出现次数的前缀和,和每个数和的前缀和,然后再枚举最大公约数
x
x
x,然后求
(
(
k
−
1
)
x
,
k
x
]
((k-1)x,kx]
((k−1)x,kx]这个区间的和,并用
k
x
∗
c
n
t
−
这
个
区
间
的
和
kx*cnt-这个区间的和
kx∗cnt−这个区间的和,枚举x时,还要在内部不停的枚举其系数
k
k
k
#include <iostream>
#include <vector>
using namespace std;
#define ll long long
const ll N = 300010;
ll n, k;
ll cnt[N], sum[N];
ll p_cnt[N + 1], p_sum[N + 1];
int main() {
cin >> n >> k;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
cnt[x] += 1;
sum[x] += x;
}
for (int i = 1; i <= N; i++) {
p_cnt[i] = p_cnt[i - 1] + cnt[i];
p_sum[i] = p_sum[i - 1] + sum[i];
}
ll ans = 1;
for (int i = 2; i < N; ++i) {
ll L = 1, R = i;
ll Cost = 0;
while (L < N) {
ll c_cnt = p_cnt[min(R, N)] - p_cnt[L - 1];
ll s_sum = p_sum[min(R, N)] - p_sum<以上是关于AtCoder Regular Contest 126(补题)的主要内容,如果未能解决你的问题,请参考以下文章