1099Div2-构造+模拟+贪心
1099Div2
Dashboard - Codeforces Round 1099 (Div. 2) - Codeforces
Easy:A
Mid:B
Hard-Mid:C
Hard:D(Gemini3.5Flash)
A-构造
题意:
给定一个整数n,构造出一个整数数组a1,…an,该数组具有如下性质:
数组中所有元素及相邻元素之和两两不同
题解:
一种较为愚蠢的做法是用一个set记录插入数组中的元素:
for _ in range(int(input())): n = int(input()) ans = [0]*n s = set() j = 0 i = 1 while j < n: if i not in s: ans[j] = i j += 1 i += 1 s.add(i) else: i += 1 continue print(*ans)实际上,只需要数组中所有元素均为奇数即可
for tt in range(int(input())): n = int(input()) ans = [2 * i + 1 for i in range(n)] print(*ans)还有一种可行的构造是,该数组中的所有元素均不能被3整除,在这个数组中,相邻元素的和是可以被3整除的,模3的余数形成1,2,1,2的规律
B-贪心
题意:
给定一个数组,可以最多对该数组执行如下的一次操作:
选择一个整数,以及一个子序列,将加到该子序列的每个元素上
判断是否能够是数组按非递减顺序排序
题解:
当数组中相邻元素出现递减a[i] < a[i-1]时,如果a[i-1] < a[i-2],那么显然,无法通过给定的操作实现非递减;反之,用一个maxadd = max(maxadd,a[i-1] - a[i]),记录最大的值
正向遍历数组,为每个a[i] < a[i-1]的a[i] += maxadd;
正向遍历数组,如果该数组中仍然出现递减,则无法通过指定操作实现目的。
# 对于下降的地方,记录需要添加的最小值,最大化这个最小值# 为每个下降的地方加上这个最大化的最小值# 对于连续下降的直接NO
t = int(input())for _ in range(t): n = int(input()) a = list(map(int, input().split())) ok = 1 maxadd = 0 for i in range(1,n): if a[i] < a[i-1]: # 如果连续下降,则一个无法完成 if i - 1 > 0 and a[i-1] < a[i-2]: ok = 0 maxadd = max(maxadd,a[i-1] - a[i]) if not ok: print("NO") else: for i in range(1,n): if a[i] < a[i-1]: a[i] += maxadd for i in range(1,n): if a[i] < a[i-1]: ok = 0 if ok: print("YES") else: print("NO")#include<stdio.h>#include<stdlib.h>void solve(){ int n; if(scanf("%d",&n) != 1){ return; } long long *a = (long long *)malloc(n*sizeof(long long)); for(int i = 0;i < n;i++){ scanf("%lld",&a[i]); } int ok = 1; long long maxadd = 0; for(int i = 1;i < n;i++){ if(a[i] < a[i-1]){ if(i-1 > 0 && a[i-1] < a[i-2]){ ok = 0; } long long diff = a[i-1] - a[i]; if(diff > maxadd){ maxadd = diff; } } } if (!ok){ printf("NO\n"); } else{ for(int i = 1;i<n;i++){ if(a[i] < a[i-1]){ a[i] += maxadd; } } for(int i=1;i<n;i++){ if(a[i] < a[i-1]){ ok = 0; } } if(ok){ printf("YES\n"); } else{ printf("NO\n"); } } free(a);}
int main(){ int t; if(scanf("%d",&t) != 1){ return 0; } while(t--){ solve(); } return 0;}C-模拟+思维
题意:
给定一个序列,选择一个元素进行如下操作:
1.如果所选的元素是偶数,则将其除以2
2.如果所选的元素是奇数,则将其加1
求出最小的操作次数,使得给定的序列中所有数都相等
题解:
注意到,对一个数进行上次多次操作后,该数会达到1,随后在进行操作,数在[1,2,1,2…]变换,
因此只需要保留该数在达到1之前的前缀部分即可
用一个字典reach记录数组中所有数字经过操作后得到的数字,包括两个字段:某个演变出的数字num有[went, steps],分别代表有多少个原始数组中的元素经过操作可以到达num;所有可到达该数的总操作次数
最后比较所有went == n的情况下,最小的steps
for _ in range(int(input())): n = int(input()) a = list(map(int,input().split())) reach = {} for num in a: went_to = set() step = 0 while num not in went_to: went_to.add(num) if num not in reach: reach[num] = [1,step] else: reach[num][0] += 1 reach[num][1] += step if num%2 == 0: num //= 2 else: num += 1 step += 1 ans = float('inf') for went,step in reach.values(): if went == n and step < ans: ans = step print(ans)from collections import defaultdictfor _ in range(int(input())): n = int(input()) a = list(map(int,input().split())) reach = defaultdict(lambda: [0, 0]) for x in a: step = 0 s = set() while x not in s: s.add(x) reach[x][0] += 1 reach[x][1] += step if x % 2 == 0: x//=2 else: x+=1 step += 1 ans = float('inf') for cnt,tot in reach.values(): if cnt == n and tot < ans: ans = tot print(ans)#include <stdio.h>#include <stdlib.h>#include <string.h>
#define INF 1000000000000000000LL
typedef struct { long long key; long long went; long long step_sum; int is_used;} ReachNode;
#define REACH_SIZE 5000005ReachNode reach[REACH_SIZE];
int used_slots[REACH_SIZE];int used_slots_cnt = 0;
void reset_reach_fast() { for (int i = 0; i < used_slots_cnt; i++) { reach[used_slots[i]].is_used = 0; reach[used_slots[i]].key = 0; reach[used_slots[i]].went = 0; reach[used_slots[i]].step_sum = 0; } used_slots_cnt = 0;}unsigned int hash_p68(unsigned long long key) { key ^= key >> 33; key *= 0xff51afd7ed558ccdULL; key ^= key >> 33; return key % REACH_SIZE;}
void add_to_reach(long long num, long long step) { unsigned int hash = hash_p68((unsigned long long)num); while (reach[hash].is_used && reach[hash].key != num) { hash = (hash + 1) % REACH_SIZE; }
if (!reach[hash].is_used) { reach[hash].key = num; reach[hash].went = 1; reach[hash].step_sum = step; reach[hash].is_used = 1; used_slots[used_slots_cnt++] = hash; } else { reach[hash].went += 1; reach[hash].step_sum += step; }}
#define SET_SIZE 205long long went_to[SET_SIZE];int set_used[SET_SIZE];
void reset_set() { memset(set_used, 0, sizeof(set_used));}
int check_and_add_set(long long num) { unsigned long long hash = (unsigned long long)num % SET_SIZE; while (set_used[hash] && went_to[hash] != num) { hash = (hash + 1) % SET_SIZE; }
if (set_used[hash]) { return 1; }
went_to[hash] = num; set_used[hash] = 1; return 0;}
void solve(){ int n; if (scanf("%d", &n) != 1) return;
long long *a = (long long *)malloc(n * sizeof(long long)); for (int i = 0; i < n; i++) { scanf("%lld", &a[i]); }
reset_reach_fast();
for (int i = 0; i < n; i++) { long long num = a[i]; reset_set(); long long step = 0;
while (check_and_add_set(num) == 0) { add_to_reach(num, step); if (num % 2 == 0) { num /= 2; } else { num += 1; } step += 1; } }
long long ans = INF; for (int i = 0; i < used_slots_cnt; i++) { int idx = used_slots[i]; if (reach[idx].went == n && reach[idx].step_sum < ans) { ans = reach[idx].step_sum; } }
if (ans == INF) { printf("-1\n"); } else { printf("%lld\n", ans); }
free(a);}
int main(){ int t; if (scanf("%d", &t) != 1) return 0; while (t--) { solve(); } return 0;}D-构造+贪心+模拟(Gemini3.5Flash)
题意:
给定一个长度为的原数组,我们定义:
- 前缀和数组:
- 前缀最大值数组:
现在,原数组的部分元素丢失了。输入会给你一个二进制字符串和不完整的数组:
- 如果,说明没丢,给你的是它的真实值。
- 如果,说明丢失了(此时输入给出的作为占位符),你可以将其填入任意整数。
另外,完整的前缀最大值数组 是保存完好的。
你的任务: 判断能否通过合理地填补的那些,来恢复出一个满足前缀最大值刚好为的原数组。如果能,输出Yes并打印出完整的数组;否则输出No。
题解:
代码的核心切入点在于利用 的性质。
1. 核心观察与关键推导
前缀最大值数组具有天然的单调递增性。当发生变化时,即,这意味着 刷新了历史最高纪录。因此:
一旦的确切值被固定了,我们可以往前逆推。如果且,那么根据前缀和关系,有。以此类推,只要前面连续的都是'1',那么这一段的 链条全部可以被唯一确定。
2. 代码变量的职责
b[i]:代表推导出的前缀和 的确切值。d[i]:状态标记布尔数组。d[i] = 1表示的值已经被唯一锁定了;d[i] = 0表示的值目前还是未知的(可以由 的空位来调整)。
3. 双阶段逻辑拆解
-
第一阶段:贪心逆推锁定(从前向后遇到突变,向后逆推)
遍历整个数组,一旦发现:
- 如果,不符合前缀最大值的单调性,直接判定为
No。 - 否则,令。然后从开始往左回溯:只要,说明这一步的变换贡献是已知的,因此也随之被唯一锁定为。
- 冲突检查:如果在左推过程中发现早已被另一个链条锁定过(即
d[j-1] == 1),且新算出的值不等于旧值,说明产生逻辑悖论,直接判定为No。
- 如果,不符合前缀最大值的单调性,直接判定为
-
第二阶段:正向顺推填补与合法性检验
- 从到正向遍历。如果且前一步的是已知的,那么当前位置也可以顺推出来:。
- 如果到某一步(即还没有被强制规定),说明这是一个“自由位”,由于它不影响刷新,为了最大安全化,我们把它设为一个极小值(代码里用了),使其不干扰后续的最大值。
- 终极校验:根据补全后的数组还原出最终的。并检查每一个位置是否满足。如果完全吻合,则构造成功输出
Yes;否则输出No。
for _ in range(int(input())): n = int(input()) s = ' ' + input().strip() a = [0] + list(map(int,input().split())) c = [0] + list(map(int,input().split())) b = [0] * (n + 1) d = [0] * (n + 1) d[0] = 1 b[0] = 0 c[0] = -10 ** 18 ok = True
# 第一阶段:逆推锁定 for i in range(1, n + 1): if c[i] != c[i - 1]: if c[i] < c[i - 1]: ok = False break
b[i] = c[i] d[i] = 1 j = i while j >= 0: if s[j] == '1': if d[j - 1] and b[j - 1] + a[j] != b[j]: ok = False break d[j - 1] = 1 b[j - 1] = b[j] - a[j] j -= 1 else: break if not ok: break
if not ok: print("No") continue
# 第二阶段:正向顺推与最终检验 for i in range(1, n + 1): if s[i] == '1': b[i] = b[i - 1] + a[i] d[i] = 1 if not d[i]: b[i] = -10 ** 16
a[i] = b[i] - b[i - 1]
if max(c[i - 1], b[i]) != c[i]: ok = False break
if not ok: print("No") else: print("Yes") print(' '.join(map(str, a[1:n + 1])))#include <stdio.h>#include <stdlib.h>#include <string.h>
#define N 500005long long a[N], c[N], b[N], d[N];char s[N];
long long max_ll(long long x, long long y) { return x > y ? x : y;}
void solve() { int n; if (scanf("%d", &n) != 1) return;
// 读入字符串并存放在 s[1] 开始的位置 scanf("%s", s + 1);
d[0] = 1; for (int i = 1; i <= n; i++) { scanf("%lld", &a[i]); d[i] = 0; } for (int i = 1; i <= n; i++) { scanf("%lld", &c[i]); }
c[0] = -1000000000000000000LL; // -1e18 b[0] = 0;
// 第一阶段:逆推锁定 for (int i = 1; i <= n; i++) { if (c[i] != c[i - 1]) { if (c[i] < c[i - 1]) { printf("No\n"); return; } b[i] = c[i]; d[i] = 1; for (int j = i; j >= 0; j--) { if (s[j] == '1') { if (d[j - 1] && b[j - 1] + a[j] != b[j]) { printf("No\n"); return; } d[j - 1] = 1; b[j - 1] = b[j] - a[j]; } else { break; } } } }
// 第二阶段:正向顺推与最终检验 for (int i = 1; i <= n; i++) { if (s[i] == '1') { b[i] = b[i - 1] + a[i]; d[i] = 1; } if (!d[i]) { b[i] = -10000000000000000LL; // -1e16 } a[i] = b[i] - b[i - 1]; if (max_ll(c[i - 1], b[i]) != c[i]) { printf("No\n"); return; } }
printf("Yes\n"); for (int i = 1; i <= n; i++) { printf("%lld%c", a[i], (i == n) ? '\n' : ' '); }}
int main() { // 优化标准 C 的输入输出性能 setvbuf(stdin, NULL, _IOFBF, 16384); setvbuf(stdout, NULL, _IOFBF, 16384);
int t; if (scanf("%d", &t) == 1) { while (t--) { solve(); } } return 0;}