题目大意
有两个长度为nnn的序列a,ba,ba,b,这两个序列都是单调不降的。
你可以对aaa进行不超过mmm次操作,每次操作你可以选择一个iii满足1≤i≤n1\leq i\leq n1≤i≤n,然后选择一个整数(可以是负数)xxx,将aia_iai加上xxx,这次操作要花费x2x^2x2的代价。
在操作的过程中,你需要保证aaa始终单调不降。
最后,你需要将aaa序列变为bbb序列,即对任意iii满足1≤i≤n1\leq i\leq n1≤i≤n,都有ai=bia_i=b_iai=bi。
求需要花费的总代价的最小值。输出答案模998244353998244353998244353后的值。如果不可能让aaa序列变为bbb序列,则输出−1-1−1。
1≤n,m≤105,0≤ai,bi≤1091\leq n,m\leq 10^5,0\leq a_i,b_i\leq 10^91≤n,m≤105,0≤ai,bi≤109
题解
首先,我们可以对每个值不等于bib_ibi的aia_iai都加上bi−aib_i-a_ibi−ai。我们发现,对于相邻的两个位置,我们可以规定一个修改的先后顺序以满足在修改的时候这两个位置始终保持前一个位置的aaa值不超过后一个位置的aaa值。那么,因为这些先后顺序不会有环,所以这样是可以保证aaa始终单调不降的。
我们按上面的方法操作,如果操作次数不够就输出−1-1−1。
如果还剩下一些操作次数,则我们可以用这些操作次数来减少代价。
根据基本不等式,我们将一个+x+x+x尽量平均地分成多次加法能使代价最少。那么,对于每个位置要加的xxx,我们记录它当前被拆成了多少份,设拆成了kkk份,我们求出将其拆成k+1k+1k+1份相比于kkk份能将代价减少多少。一开始每种操作都可以看作被拆成一份,我们以减少的代价为关键字来将这些操作放在大根堆里,每次取出堆顶并将代价减少对应的量,然后更新这次操作被拆成的数kkk,即令k=k+1k=k+1k=k+1,然后继续算出将其拆成k+1k+1k+1份相比于kkk份能将代价减少多少并放入堆中,这样将剩下的操作都用完,即可得出答案。
时间复杂度为O((n+m)logn)O((n+m)\log n)O((n+m)logn)。
code
#include<bits/stdc++.h>
using namespace std;
const int N=100000;
const long long mod=998244353;
int n,m;
long long ans=0,a[N+5],b[N+5];
struct node{
long long x,k,v;
bool operator<(const node ax)const{
return v<ax.v;
}
};
priority_queue<node>q;
long long dv(long long x,long long k){
return (x/k)*(x/k)*(k-x%k)+(x/k+1)*(x/k+1)*(x%k);
}
long long gt(long long x,long long k){
return dv(x,k)-dv(x,k+1);
}
int main()
{
// freopen("attend.in","r",stdin);
// freopen("attend.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
for(int i=1;i<=n;i++){
if(a[i]!=b[i]){
--m;
ans=(ans+(a[i]-b[i])*(a[i]-b[i])%mod)%mod;
q.push((node){abs(a[i]-b[i]),1,gt(abs(a[i]-b[i]),1)});
}
}
if(m<0){
printf("-1");
return 0;
}
if(q.empty()){
printf("%lld",ans);
return 0;
}
while(m--){
node t=q.top();q.pop();
ans=(ans-t.v%mod+mod)%mod;
q.push((node){t.x,t.k+1,gt(t.x,t.k+1)});
}
printf("%lld",ans);
return 0;
}
1175

被折叠的 条评论
为什么被折叠?



