https://codeforces.com/problemset/problem/1344/D
这题晚上unr以后再群里讨论,牛逼网友说了对增值二分后,我还是没想出来是什么意思,最后还是看了题解。。。
对于每一个i,我们设f(x)=x(a[i]-x^2),a[i]为常数,那么f'(x)=a[i]-3x^2,他的导数是递减的,所以对于f(x)->f(x+1)这个增量,是递减的。
那么我们队这个增量进行二分,对于每个i , 当前已经拿了b[i],如果拿b[i]+1大于这个增量delta,那么就让b[i]++
由于这个增量是递减的,所以我们可以对b[i]进行二分,当然如果无论如何都<delta,那么让b[i]=0
这个二分是有点奇怪的,我们令在delta情况下,一共有sum个,那么sum>=k,说明合法,此时移动l,因为增量是递减的,所以增量越小,个数越多。
由于最后二分出来的答案,sum可能大于k,因为有些时候不同的i的增量可能一样,那么由于能输出任意一种情况,所以直接记录一下最后二分出来答案下哪些增量是等于答案的,他们是可以减去的。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxl=3e5+10;
int n,m,cas;
ll k,ans;
ll a[maxl],b[maxl];
char s[maxl];
bool eqlans[maxl];
inline void prework()
{
scanf("%d%lld",&n,&k);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
}
inline ll calc(int i,ll x)
{
return a[i]-3*x*x+3*x-1;
}
inline ll jug(ll delta)
{
ll sum=0,l,r,mid,d;
for(int i=1;i<=n;i++)
{
l=1,r=a[i];
if(calc(i,1)<delta)
{
b[i]=0;eqlans[i]=false;
continue;
}
while(l+1<r)
{
mid=(l+r)>>1;
if(calc(i,mid)>=delta)
l=mid;
else
r=mid;
}
if(calc(i,r)>=delta)
d=r;
else
d=r-1;
b[i]=d;
eqlans[i]=(calc(i,d)==delta);
sum+=d;
}
return sum;
}
inline void mainwork()
{
ll l=-4e18,r=1e9,mid;
while(l+1<r)
{
mid=(l+r)>>1;
if(jug(mid)>=k)
l=mid;
else
r=mid;
}
if(jug(r)>=k)
ans=r;
else if(jug(r-1)>=k)
ans=r-1;
k-=jug(ans);
for(int i=1;i<=n && k<0;i++)
if(eqlans[i] && b[i]>0)
++k,--b[i];
}
inline void print()
{
for(int i=1;i<=n;i++)
printf("%lld%c",b[i],(i==n)?'\n':' ');
}
int main()
{
int t=1;
//scanf("%d",&t);
for(cas=1;cas<=t;cas++)
{
prework();
mainwork();
print();
}
return 0;
}