原题链接
题目描述
给你一个由n
个整数组成的数组。确定是否有可能用两种颜色给数组中的所有元素着色,使得两种颜色元素的和具有相同的奇偶性,如果可以输出YES
,否则输出NO
。思路
- 判断奇数的个数,如果是奇数个,那么永远无法满足题意,如果是偶数个,一定可以满足题意。
public static void solve() throws IOException {
int n = readInt();
int cnt = 0;
for (int i = 0; i < n; i++) {
int a = readInt();
if (a % 2 != 0) cnt++;
}
printWriter.println(cnt % 2 == 0 ? "YES" : "NO");
}
原题链接
题目描述
给定一个自然数x
,你可以从x
的第k
位进行四舍五入(最右边为第0
位),四舍五入的规则如下:
- 如果第
(k−1)
位上的数字大于或等于5
,则第k
位上的数字增加1
,否则第k
位上的数字保持不变- 如果在运算之前,第
k
位上的数字是9
,而且它会被增加1
,那么我们寻找最小的位置k′
(k′>k),第k′
位置的数字小于9
,并在第k′
位置的数字上加上1
。- 之后,所有位置小于
k
的数字都被替换为0
输出
x
的最大值样例输入
12 //测试数据的个数 1 5 99 913 1980 20444 20445 60947 419860 40862016542130810467 45 445
样例输出
1 10 100 1000 2000 20444 21000 100000 420000 41000000000000000000 100 1000
思路
分类讨论
- 如果这个数的最高位大于等于
5
,那么可以直接在最高位四舍五入(第八个样例)- 判断所有位是不是都小于
5
,如果是,那么无法四舍五入,输出原数(第六个样例)- 找第一个大于等于
5
的数的位置i
,那么i
以及比i
低的位都将变为0
,再看比i
高的位看是否等于4
,如果等于4
说明比i
更高的位+1后也可以进位(4+1->5,5进位->0第十二个样例),那么继续枚举,如果不等于4
,那么低i+1
位+1后,更高的位直接输出原数(第七个样例)- 特判进位后全为
0
的情况,第十一个样例
public static void solve() throws IOException {
String s = readString();
if (s.charAt(0) >= '5') {//第一位就大于5
int len = s.length();
printWriter.print("1");
for (int i = 0; i < len; i++) {
printWriter.print("0");
}
printWriter.println();
} else {
boolean f = false;//是否有大于5的数
int idx = 0;//第一个大于等于5的位置
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) >= '5') {
idx = i;
f = true;
break;
}
}
if (!f) {//全小于5
printWriter.println(s);
} else {
StringBuilder sb = new StringBuilder();
for (int i = s.length() - 1; i >= idx; i--) {
sb.append("0");//当前位和更低位变为0
}
boolean flag = false;
for (int i = idx - 1; i >= 0; i--) {
if (flag) {//更高的位都无法进位了
int p = s.charAt(i) - '0';
sb.append(String.valueOf(p));
continue;
}
if (s.charAt(i) == '4') {
sb.append("0");//是4,继续枚举更高位
} else {
flag = true;//更高的位都无法进位了
int p = s.charAt(i) - '0' + 1;
sb.append(String.valueOf(p));
}
}
String t = sb.reverse().toString();
if (t.charAt(0) == '0') {
t = "1" + t;//特判进位后全为0的情况
}
printWriter.println(t);
}
}
}
原题链接
题目描述
萨沙有一个长度为n
的整数数组a
,他记录下了所有 i 、 j ( i < j ) i、j(ii、j(i<j) 的最小值 a i a_i ai和 a j a_j aj后组成了一个长度为 n ∗ ( n − 1 ) 2 \frac {n * (n - 1)} {2} 2n∗(n−1)的新数组b
(数组b
是的顺序是随机的,也就是说你可以以任意顺序返回数组a
)。还原数组a
。样例输入
5 //测试数据的个数 3 1 3 1 2 10 4 7 5 3 5 3 3 5 2 2 2 2 2 2 2 2 2 2 5 3 0 0 -2 0 -2 0 0 -2 -2
样例输出
1 3 3 10 10 7 5 3 12 2 2 2 2 2 0 -2 0 3 5
思路
- 将数组
b
从小到大排序,统计每个数字出现的次数,如果剩余出现的次数大于等于还需要比较的数的个数,那么这个数就是数组a
的元素之一
public static void solve() throws IOException {
int n = readInt();
Map<Integer, Integer> map = new HashMap<>();
int q = n * (n - 1) / 2;//数组b的长度
int[] arr = Utils.nextIntArray(q);
Arrays.sort(arr, 1 , q + 1);
for (int i = 1; i <= q; i++) {
map.put(arr[i], map.getOrDefault(arr[i], 0) + 1);
}
List<Integer> list = new ArrayList<>();
int p = n;// 还需要比较的数的个数
for (int i = 1; i <= q; i++) {
// 剩余出现的次数大于等于还需要比较的数的个数,那么这个数就是数组a的元素之一
if (map.get(arr[i]) > 0 && map.get(arr[i]) >= (p - 1)) {
list.add(arr[i]);
map.put(arr[i], map.getOrDefault(arr[i], 0) - (p - 1));
p--;
}
}
//数组 a的长度还没有达到 q,那就用数组 b的最后一个数弥补
for (int i = 0; i < p; i++) {
list.add(arr[q]);
}
for (int i = 0; i < n; i++) {
printWriter.print(list.get(i) + " ");
}
printWriter.println();
}
原题链接
题目描述
给定两个长度为n
的数组a
和b
,数组下标都是从 1 ∼ n 1 \sim n 1∼n。现在要求你构建一个有向图,如果 a u − a v ≥ b u − b v a_u - a_v \ge b_u - b_v au−av≥bu−bv,那么就存在一条u
指向v
的边。如果一个点可以到达图中的任意一个点,那么这个点就是强顶点,求出所有强顶点的个数和编号。样例输入
5 //测试数据的个数 4 3 1 2 4 4 3 2 1 5 1 2 4 1 2 5 2 3 3 1 2 1 2 2 1 3 0 2 1 1 3 2 3 5 7 4 -2 -3 -6
样例输出
1 4 2 3 5 1 2 3 1 2 3 2 2 3
思路
- 公式转换:其中的 a u − a v ≥ b u − b v a_u - a_v \ge b_u - b_v au−av≥bu−bv可以转换为 a u − b u ≥ a v − b v a_u - b_u \ge a_v - b_v au−bu≥av−bv,那么就相当于求 a i − b i a_i - b_i ai−bi的最大值,这样可以满足任意的 a j − b j ≤ a i − b i a_j - b_j \le a_i - b_i aj−bj≤ai−bi,这样点
i
可以到达任意点j
public static void solve() throws IOException {
int n = readInt();
int[] arr1 = Utils.nextIntArray(n), arr2 = Utils.nextIntArray(n);
long max = Long.MIN_VALUE;
for (int i = 1; i <= n; i++) {
max = Math.max(max, arr1[i] - arr2[i]);//求最大值
}
List<Integer> list = new ArrayList<>();
for (int i = 1; i <= n; i++) {
if (arr1[i] - arr2[i] == max){
list.add(i);// 所有的强顶点
}
}
printWriter.println(list.size());
for (int p : list) {
printWriter.print(p + " ");
}
printWriter.println();
}
原题链接
题目描述
给定一条数轴上的n
个点,对于其中的每个点s
,它将与所有点x
(包括s
自己)分别构成一条条线段,这条线段能访问到 x ∼ s ( x ≤ s ) x \sim s(x \le s) x∼s(x≤s)或者 s ∼ x ( s ≤ x ) s \sim x(s \le x) s∼x(s≤x)上的点,你需要求出数轴中的每个点作为s
时,所有点被访问到的次数之和。输入样例
3 //测试数据的个数 3 1 4 3 5 1 2 5 7 1 4 1 10 100 1000
输出样例
8 7 6 16 15 18 24 16 1111 1093 1093 2893
思路:排序+前缀和+数学
- 先将数组排序,用一个 pair记录数字在原数中的位置就不影响答案。
- 对于第
i
个数s
– 先计算比s
小的数x
的个数有(i - 1)个,我们假设这些比s
小的数的都有 1 ∼ s 1 \sim s 1∼s的贡献值,那么总贡献为(i - 1) * s
,因为有些数不能贡献那么多,所以需要将多加的贡献值减去,需要减去的贡献值就是pre[i - 1]
,减完发现多减去了i - 1
个1
的贡献值,所以最后还需要加上。
– 再计算比s
大的数x
的个数有(n - i)个,我们假设这些比s
大的数的都有 1 ∼ x 1 \sim x 1∼x的贡献值,那么总贡献为suf[i + 1]
,因为有些数不能贡献那么多,所以需要将多加的贡献值减去,需要减去的贡献值就是s * (n - i)
,减完发现多减去了n - i
个1
的贡献值,所以最后还需要加上。
– 最后加上s
本身贡献的一个。
public static void solve() throws IOException {
int n = readInt();
Pair[] pairs = new Pair[n + 1];
Arrays.setAll(pairs, g -> new Pair());
long[] pre = new long[n + 5], suf = new long[n + 5];
for (int i = 1; i <= n; i++) {
pairs[i].first = readInt(); pairs[i].second = i;
}
Arrays.sort(pairs, (o1, o2) -> o1.first - o2.first);
for (int i = 1, j = n; i <= n; i++, j--) {
pre[i] = pre[i - 1] + pairs[i].first;
suf[j] = suf[j + 1] + pairs[j].first;
}
long[] res = new long[n + 1];
for (int i = 1; i <= n; i++) {
res[pairs[i].second] = ((long) pairs[i].first * (i - 1) - pre[i - 1] + (i - 1)) +
(suf[i + 1] - (long) pairs[i].first * (n - i) + (n - i)) + 1;
}
for (int i = 1; i <= n; i++) printWriter.print(res[i] + " ");
printWriter.println();
}