【好题】数学+二分+思维——cf1344D/1345F

/*
对于每个bi,当bi从x-1提升到x时,对答案的贡献是 ai-3x^2+3x-1, x>=1,函数递减
即bi越大时,bi+1所获得的增量收益就越低

所以有暴力的解法就是每次找到当前+1后增量最大的那个bi,给其+1 

考虑二分这个增量delta,对每个bi,二分找到x,当bi从x-1增加到x时,获得的增量>=delta
judge时把所有bi加起来,如果sum<=k,那么delta可以更小,反之delta要更大
最后的解之和sum可能不到k,那么找这组解中增量最大的k-sum个数+1即可 

另外记得二分上下界要开的大一点
*/ #include<bits/stdc++.h> using namespace std; #define N 200005 #define ll long long ll sum,n,a[N],k,b[N]; double f(ll a,ll x){ return a-3.0*x*x+3.0*x-1.0; } int judge(ll delta){ sum=0; for(int i=1;i<=n;i++){ ll L=0,R=a[i],mid; b[i]=0; while(L<=R){ mid=L+R>>1; if(f(a[i],mid)>=delta) b[i]=mid,L=mid+1; else R=mid-1; } } for(int i=1;i<=n;i++)sum+=b[i]; if(sum<=k)return 1; return 0; } struct Node{ll id,v,up;}p[N]; int cmp1(Node a,Node b){return a.up>b.up;} int cmp2(Node a,Node b){return a.id<b.id;} int main(){ cin>>n>>k; for(int i=1;i<=n;i++)cin>>a[i]; ll L=-4e18,R=1e18,mid,ans; while(L<=R){ mid=L+R>>1; if(judge(mid))//增量还可以更小 ans=mid,R=mid-1; else L=mid+1; } judge(ans); if(sum==k){ for(int i=1;i<=n;i++)cout<<b[i]<<" "; }else if(sum<k){ for(int i=1;i<=n;i++) p[i].id=i,p[i].v=b[i],p[i].up=f(a[i],b[i]+1); sort(p+1,p+1+n,cmp1); for(int i=1;i<=n;i++)if(p[i].v<a[p[i].id]){ p[i].v++; if(++sum==k)break; } sort(p+1,p+1+n,cmp2); for(int i=1;i<=n;i++)cout<<p[i].v<<' '; } }
原文地址:https://www.cnblogs.com/zsben991126/p/12850647.html