Résumé Review 二分方法题解

 

 一道非常好的数学题~

题意

这里就要夸一下某谷了,翻译的很好,不像我,在CF上用deepl翻译,不够清晰(←全是废话)

 

分析

先不考虑 bi ,考虑转换为将k1分配到每一个 bi 中(因为 bi ∈ N*),定义f(x,i) = ai x + x3 ,m = 1

则可以得出

Δf(x,i) = f(x + m,i) - f(x,i) = ai - 3x2 + 3x - 1

易得暴力:O(N)地去枚举每个1给哪个bi ,显然此时的 Δf(x,i) = ans = maxni = 1  Δf(x,i)

但是暴力的时间复杂度为O(NK),很明显,不行

再回归Δf(x,i) = f(x + m,i) - f(x,i) = ai - 3x2 + 3x - 1,x∈N* 

注意x∈N* 

在将Δf的函数图画出来之后,发现,只要存在x∈N* ,Δf单调递减,所以在暴力的做法中,每次增加的Δf(x,i) 一定也是递减

我们定义最大的 Δf(x,i) = p,也就是在暴力做法中第一次加的答案,之后的操作所增加的Δf(x,i)一定都不会超过p,由此,得出满足条件的最大的每一个bi ,可以看出,p具有单调性,想到了什么?二分!!!

所以直接二分p,可以求bi 的过程可以用二分,也可以用解一元二次方程的方法做

 

但是,这样使用二分,不一定使b序列之和 = k成立,所以剩下的一小部分,暴力求得最优解,同时计算Δf(x,i)

 

 1 #include"bits/stdc++.h"
 2 using namespace std;
 3 #define ll long long
 4 const ll N = 200010,inf = 1e18;
 5 #define inl inline
 6 #define regi register int
 7 ll n,k;
 8 ll l,r,mid,sm;
 9 ll a[N],v[N];
10 inl ll read(void)
11 {
12     ll x = 0,f = 1;char ch = getchar();
13     while(!isdigit(ch)) f = ch == '-' ? - 1 : f,ch = getchar();
14     while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0',ch = getchar();
15     return x * f;
16 }
17 inl ll f(ll u,ll x);
18 inl ll calc(ll x,ll lim);
19 inl ll check(ll mid);
20 int main(void)
21 {
22     n = read(),k = read();
23     for(regi i = 1;i <= n;i ++)
24     {
25         a[i] = read(),l = min(l,f(a[i],a[i] - 1)),r = max(r,f(a[i],0));
26     }
27     while(r - l >= 2)
28     {
29         mid = (l + r) >> 1;
30         check(mid) ? r = mid : l = mid;
31     }
32     if(check(l))
33     {
34         r = l;
35     }
36     check(r);
37     k -= sm;
38     
39     for(regi i = 1;i <= n;i ++)
40     
41         if(k && v[i] < a[i] && f(a[i],v[i]) == r)    v[i] ++,k --;
42     
43     for(regi i = 1;i <= n;i ++)
44     {
45         printf("%lld ",v[i]);
46     }
47     return 0;
48 }
49 
50 inl ll f(ll u,ll x)
51 {
52     return u == x ? inf : u - 3 * x * x + 3 * x - 1;
53 }
54 inl ll calc(ll x,ll lim)
55 {
56     ll l = 1,r = a[x],mid,res = a[x];
57     while(l <= r)
58     {
59         mid = (l + r) >> 1;
60         if(f(a[x],mid) <= lim)
61         
62             r = mid - 1,res = mid;
63         else
64             l = mid + 1;
65     }
66     return res;
67 }
68 inl ll check(ll mid)
69 {
70     sm = 0;
71     for(regi i=1;i <= n;i ++)
72     {
73         sm += (v[i] = calc(i,mid));
74     }
75     return sm < k;
76 }

 

posted @ 2024-01-06 16:48  Ech0_7  阅读(17)  评论(0)    收藏  举报