ACM - CF - 1496B - 二分
题意:
给定一n个数的数组,数组元素不重复,给定k次操作。问:k次操作之后,数组中不重复的元素个数是多少。
操作定义为:(max{} + mex{}) / 2向上取整。
其中:max{}是数组中最大的数,mex{}是数组中第一个未出现的自然数。
样例解释:
4 1
0 1 3 4
1次操作,mex{} = 2, max{} = 4, 添加3,答案是4
3 1
0 1 4
3 0
0 1 4
0次操作,答案是3
3 2
0 1 2
两次操作,mex{}=3, max{}=2, 添加3;mex{}=4,max=3,添加4,答案是5
3 2
1 2 3
两次操作,mex{}=0, max{}=3, 添加2,变成{1 2 2 3};第二次操作,mex{}=0, max{}=3,变成{1 2 2 2 3},答案是3
两个点:
(1)mex{}怎么计算,即找第一个未出现的自然数
数组排序。若a[i] = i,说明在i之前都是有序的,且都出现了。即二分
(2)题目的k次,骗人的。重点是不同的数。
观察后两个样例。
第一个,由于出现了 0 ~ n-1,根据规则,mex{}=n, max{}=n-1, 会添加n;所以第二次添加n+1,以此类推
第二个,由于2是重复出现的,所以之后都不用再算。k那么大,一直算,会超时的
这两个即为边界条件
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 20; int T,n,k; int a[maxn]; int main(){ //freopen("input.txt", "r", stdin); scanf("%d",&T); while(T--){ memset(a, 0, sizeof(a)); scanf("%d%d", &n, &k); for(int i = 0; i < n; i++) scanf("%d", &a[i]); sort(a, a+n); if (a[n - 1] == n - 1){ printf("%d\n", n + k); continue; } int l, r, mid, maxx = a[n - 1], tmp; //printf("%d\n", maxx); int num; for(num = 1; num <= k; num++){ //printf("num = %d\n", num); l = 0; r = n - 1 + num - 1; while(l <= r){ mid = (l + r) >> 1; //printf("%d %d\n", mid, a[mid]); if (a[mid] == mid) l = mid + 1; else r = mid - 1; } //printf("%d\n", l); tmp = (l + maxx + 1) / 2; //printf("tmp = %d maxx = %d\n", tmp, maxx); l = 0; r = n - 1 + num - 1; while(l <= r){ mid = (l + r) >> 1; if (a[mid] <= tmp) l = mid + 1; else r = mid - 1; } //printf("l,r,mid = %d %d %d %d %d %d\n", l, a[l], r, a[r], mid, a[mid]); for(int i = n - 1 + num - 1; i >= l ; i--) a[i + 1] = a[i]; a[l] = tmp; if (l > 0 && a[l - 1] == tmp) break; if (a[l + 1] == tmp) break; //sort(a, a + n + num); //for(int i = 0; i < n + num; i++) // printf("%d%c", a[i], i == n + num - 1 ? '\n' : ' '); } int cnt = 1; for(int i = 1; i < n + num; i++) if (a[i] != a[i - 1] && a[i]) cnt++; printf("%d\n", cnt); //for(int i = 0; i < n + num; i++) // printf("%d%c", a[i], i == n + num - 1 ? '\n' : ' '); } return 0; }