Codeforces Round #143 (Div. 2) C. To Add or Not to Add 二分
http://codeforces.ru/contest/231/problem/C
题意:
给你n个数,你每次可以使其中的一个数加1,最多的加1次数为k,可以选择同一个数连续加但必须小于k。求使得小于等于k次加1操作后原序列中出现的相同元素最多的元素的个数,以及这个元素的值。
思路:
比赛时想了O(n^2)的做法写了一下超时,这是必然,我们首先对其进行从小到达排序然后枚举位置i ,从i - 1到1 枚举查看加到a[i]需要加1的次数直到k剩余个数不满足。这样的O(n^2)显然超时。要怎么优化呢? 开始想了二分枚举到底枚举什么自己糊涂了,没想到。这里看对于个数枚举。当我们枚举到 i 时,在区间[1,i - 1](前提是从小到达排序)中枚举满足加小于等于k的1满足等于a[i]的个数,这里我们求需要k的个数因为需要1的个数与区间满足条件的元素的个数呈单调性。 needk = m*a[i] - (sum[i] - sum[i - m]).这样就好了。。
PS:这里的时间复杂度为O(n*logn)用cin,cout tle无语, 注意数据类型的把空
View Code
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll long long #define inf 0x7f7f7f7f #define MOD 1073741824 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 150 #define N 100007 using namespace std; //freopen("data.in","r",stdin); int a[N]; ll sum[N]; int bSearch(int l,int r,int k,int pos){ int ans = 1; while (l <= r){ int m = (l + r)>>1; ll tmp = (ll)m*a[pos] - (sum[pos] - sum[pos - m]);//here could overflow if (tmp <= k){ l = m + 1; ans = m; } else{ r = m - 1; } } return ans; } int main(){ //freopen("data.in","r",stdin); int i; int n,k; while (~scanf("%d%d",&n,&k)){ sum[0] = 0; for (i = 1; i <= n; ++i) scanf("%d",&a[i]); sort(a + 1,a + 1 + n); for (i = 1; i <= n; ++i) sum[i] = sum[i - 1] + a[i]; int ans = 1; int mk = a[1]; int tmp; for (i = 2; i <= n; ++i){ tmp = bSearch(1,i,k,i); if (ans < tmp){ ans = tmp; mk = a[i]; } } printf("%d %d\n",ans,mk); } return 0; }