0%

Codeforces Round #248 (Div. 2)

比赛链接: http://codeforces.com/contest/433

A题(Kitahara Haruki’s Gift)

题目大意:

有 n(1 <= n <= 100) 个苹果,第 i (1 <= i <= n) 个苹果的价值为 $w_i$ ($w_i$ == 100 or $w_i$ ==200), 求是否可以将苹果分成两部分,使得每一部分的价值相同。(注意:苹果不能分割)

解题思路:

题目规定了苹果的价值 w 只有 100 和 200 两种数字大大降低了题目的难度, 只需要贪心一下就可以过。先计算出所有苹果的价值总和 sum,  首先验证 sum 除以 2 后可不可以被 100 整除, 如果不能则肯定不能满足题目要求。如果可以,下面就验证一下用现有苹果可不可以凑出 sum / 2,这个价值就行了。

参考代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 110;
int a[MAXN];

int main(){
int n;
while(scanf("%d", &n) != EOF){
int sum = 0;
for(int i = 0; i < n; i++){
scanf("%d", &a[i]);
sum += a[i];
}

sum /= 2;
if(sum % 100) printf("NO\n");
else{
sort(a, a + n);
int r = n - 1;
while(sum && r >= 0){
if(sum - a[r] >= 0){
sum -= a[r];
r--;
}else break;
}
if(sum == 0) printf("YES\n");
else if(sum == 100 && a[0] == 100) printf("YES\n");
else printf("NO\n");
}
}
return 0;
}

B题(Kuriyama Mirai’s Stones)

题目大意:

题目抽象出来就是给定 n(1 <= n <= 10^5) 个数的序列 $v_i$(1 <= i <= n),有两种询问,询问 1:输出序列中区间[l, r] 内所有数的和,询问2: 输出按照非递减序列排序后序列中区间[l, r]内所有数的和.

解题思路:

维护两个前缀和一个是原来的序列的前缀和,另一个是排序之后的前缀和,然后求区间 [l, r] 中所有数的和只需要 ans = sum[r] - sum[l];

参考代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 110000;

int a[MAXN];
long long sum1[MAXN];
long long sum2[MAXN];

int main(){
int n, m;
ios_base::sync_with_stdio(false);
while(cin >> n){
for(int i = 1; i <= n; i++)
cin >> a[i];

for(int i = 1; i <= n; i++)
sum1[i] = sum1[i - 1] + a[i];

sort(a + 1, a + n + 1);

for(int i = 1; i <= n; i++)
sum2[i] = sum2[i - 1] + a[i];

cin >> m;
while(m--){
int c, l, r;
cin >> c >> l >> r;
if(c == 1) cout << sum1[r] - sum1[l - 1] << endl;
else cout << sum2[r] - sum2[l - 1] << endl;
}
}
return 0;
}

C题(Ryouko’s Memory Note)

题目大意:

给定 $n(1 <= n <= 10^5)$ 个数的一个序列 $v_i(1 <= i <=n)$ 现要求改变其中最多一个数,使得$sum=\sum_{i=1}^{m-1}\left| a_{i+1} - a_i \right|$最小。

解题思路:

对于序列中出现的每一个数,我们为他建立一个临接表,临接表里面记录着这个数所有相邻的数,由数学知识可知当这个数为临接表中所有数的中位数时,得到的差的绝对值之和最小,所以我们只要枚举序列中的每一个数将其变为所有与他相临的数的中位数,找出最佳答案即可。

参考代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 100100;

int a[MAXN];
vector <int> vec[MAXN];

inline int ABS(int x){
if(x < 0) return -x;
return x;
}

int main(){
int n, m;

while(scanf("%d%d", &n, &m) != EOF){
for(int i = 0; i < MAXN; i++)
vec[i].clear();

for(int i = 1; i <= m; i++)
scanf("%d", &a[i]);

long long sum = 0;
int max1 = a[1];
for(int i = 2; i <= m; i++){
if(a[i] == a[i - 1]) continue;
sum += ABS(a[i] - a[i - 1]);
vec[a[i - 1]].push_back(a[i]);
vec[a[i]].push_back(a[i - 1]);
max1 = max(max1, a[i]);
}

long long ans = 0, tmp1, tmp2, mid;
for(int dx = 1; dx <= max1; dx++){
tmp1 = 0, tmp2 = 0;
sort(vec[dx].begin(), vec[dx].end());
int size = vec[dx].size();
if(!size) continue;
mid = vec[dx][size / 2];
for(int j = 0; j < size; j++){
tmp1 += ABS(vec[dx][j] - dx);
tmp2 += ABS(vec[dx][j] - mid);
}
ans = max(tmp1 - tmp2, ans);
}

printf("%I64d\n", sum - ans);

}
return 0;
}