NOIP2023模拟16联测37 大眼鸹猫

题目大意

有两个长度为 n n n的序列 a , b a,b a,b,这两个序列都是单调不降的。

你可以对 a a a进行不超过 m m m次操作,每次操作你可以选择一个 i i i满足 1 ≤ i ≤ n 1\leq i\leq n 1≤i≤n,然后选择一个整数(可以是负数) x x x,将 a i a_i ai加上 x x x,这次操作要花费 x 2 x^2 x2的代价。

在操作的过程中,你需要保证 a a a始终单调不降。

最后,你需要将 a a a序列变为 b b b序列,即对任意 i i i满足 1 ≤ i ≤ n 1\leq i\leq n 1≤i≤n,都有 a i = b i a_i=b_i ai=bi。

求需要花费的总代价的最小值。输出答案模 998244353 998244353 998244353后的值。如果不可能让 a a a序列变为 b b b序列,则输出 − 1 -1 −1。

1 ≤ n , m ≤ 1 0 5 , 0 ≤ a i , b i ≤ 1 0 9 1\leq n,m\leq 10^5,0\leq a_i,b_i\leq 10^9 1≤n,m≤105,0≤ai,bi≤109

题解

首先,我们可以对每个值不等于 b i b_i bi的 a i a_i ai都加上 b i − a i b_i-a_i bi−ai。我们发现,对于相邻的两个位置,我们可以规定一个修改的先后顺序以满足在修改的时候这两个位置始终保持前一个位置的 a a a值不超过后一个位置的 a a a值。那么,因为这些先后顺序不会有环,所以这样是可以保证 a a a始终单调不降的。

我们按上面的方法操作,如果操作次数不够就输出 − 1 -1 −1。

如果还剩下一些操作次数,则我们可以用这些操作次数来减少代价。

根据基本不等式,我们将一个 + x +x +x尽量平均地分成多次加法能使代价最少。那么,对于每个位置要加的 x x x,我们记录它当前被拆成了多少份,设拆成了 k k k份,我们求出将其拆成 k + 1 k+1 k+1份相比于 k k k份能将代价减少多少。一开始每种操作都可以看作被拆成一份,我们以减少的代价为关键字来将这些操作放在大根堆里,每次取出堆顶并将代价减少对应的量,然后更新这次操作被拆成的数 k k k,即令 k = k + 1 k=k+1 k=k+1,然后继续算出将其拆成 k + 1 k+1 k+1份相比于 k k k份能将代价减少多少并放入堆中,这样将剩下的操作都用完,即可得出答案。

时间复杂度为 O ( ( n + m ) log ⁡ n ) O((n+m)\log n) O((n+m)logn)。

code

cpp 复制代码
#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;
}
相关推荐
励志不掉头发的内向程序员9 小时前
【LibreCAD 2D架构】从鼠标点击到图形创建:RS_ActionDrawLine交互流程与状态机解析
开发语言·c++·qt·学习·系统架构·计算机外设·交互
OPEN-F9 小时前
ROS2系列教程:Gazebo插件(关节控制/IMU/激光雷达)
c++·python·数码相机·算法·机器人
蒸蒸yyyyzwd9 小时前
cpp 选手秋招学习笔记 day27
服务器·c++·面试·八股
haolin123.10 小时前
STL vector底层揭秘:从构造到迭代器失效
开发语言·c++·算法
尘中远10 小时前
给C++工业软件搭建 Agent
开发语言·c++·qt·ai·agent
2401_8685347810 小时前
网规备考_2.4 路由协议
c++·设计模式
啦啦啦啦啦zzzz10 小时前
自定义日志缓冲区(上)c++20
linux·服务器·c++·网络编程
wabs66610 小时前
关于二叉树【力扣199.二叉树的右视图的思考】
数据结构·c++·算法·leetcode·二叉树·层序遍历
hope_wisdom10 小时前
C/C++数据结构之二叉树的遍历
c语言·数据结构·c++·二叉树·深度优先·广度优先
cpp_learner11 小时前
C++ 实现责任链模式(Chain of Responsibility):从一堆 if-else 到可插拔的处理管道
c++·设计模式