P8590 『JROI-8』这是新历的朝阳,也是旧历的残阳

P8590 『JROI-8』这是新历的朝阳,也是旧历的残阳

本文一开始参考了@ ScottSuperb 的博客的 codecode

挺好的思维题。

注意,满足数列 ai<ai+1a_{i}<a_{i+1}

主题的思路:贪心,数学。

正数阵营:对于分到第 kk 次,平方贡献是 (a[i]+k)2(a[i]+k)^2 的。

负数阵营:对于分到第 kk 次,平方贡献是 (a[i]+1)2(a[i]+1)^2 的。

首先,对于分成 k2k \geq 2 的段,我们为了使贡献最大,我们只可以数分成两部分,一部分放在第一段,这一段的数都是负数,因为加 11 会让他们的平方损失贡献最小。而对于某些负数,即 a+1<a+m\left\vert a+1 \right\vert<a+m 的情况,这个数在正数阵营中的贡献就更大了,我们把它归入正数阵营之中。

由于本题 n106n\leq10^6k107k\leq10^7,数据范围很大,普通的暴力必然是不可行的,考虑数学,即推式子。

前置:我们设 bb 表示第一个正数阵营出现的位置,nownow 表示当前这一分段的总和,sum1sum1 所有正数阵营的和,nn 表示正数阵营的个数,ansans 为答案。

最开始的时候,所有负数都在负数阵营。

首先考虑正数的贡献。

假设现在划分到第 mm 次,那么当前的正数贡献就是 i=xn(ai+m)2\sum_{i=x}^n(a_i+m)^2 ,到第 m+1m+1 次,正数的贡献就是i=xn(ai+m+1)2\sum_{i=x}^n(a_i+m+1)^2(多出一个空集)。拆开这两个式子作差,差就是 (i=xnai)+n(\sum_{i=x}^na_{i})+n

所以,正数的贡献可以由上一次推过来的,时间为 O(1)O(1)

对于负数,我们知道,在 a+1<a+m\left\vert a+1 \right\vert<a+m 的情况下,他可以归入正数中。那么 now+=((a[i]+m)2(a[i]+1)2)now+=((a[i]+m)^2-(a[i]+1)^2),即统计负数归入正数阵营的时候,所给与的贡献,同时 正数阵营个数加一个,第一个正数阵营出现的位置往前推,更新 ansans 的答案,即它在负数阵营和正数阵营的差值,sum1sum1 加上当前的数加上 mm 的和。

最后记得取模。

#include<bits/stdc++.h>
using namespace std;
const int N =1e6+10;
const int mod=998244353;
#define int long long
int a[N];
int n,k,b;
int now,ans,sum1,now1;
signed main()
{
// 	freopen(".in","r",stdin);
// 	freopen(".out","w",stdout);
	cin>>n>>k;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
		b+=a[i]<0;
		now=(now+(a[i]+1)*(a[i]+1))%mod;
	}
	ans+=now;
	for(int i=b;i<n;i++)
		sum1=(sum1+a[i]+1)%mod;
	n-=b;
	for(int m=2;m<=k;m++)
	{
		ans=(ans+(now=(now+sum1*2%mod+n)%mod))%mod;
		sum1=(sum1+n)%mod;
		while(b>0&&a[b-1]+m>abs(a[b-1]+1))
		{
			--b,++n;
			now1=(a[b]+m)*(a[b]+m)%mod-(a[b]+1)*(a[b]+1)%mod;
			ans=(ans+now1)%mod,now=(now+now1)%mod,sum1=(sum1+(a[b]+m))%mod;
		}
	}
	cout<<ans<<endl;
	return 0;
}
posted @   June_Failure  阅读(14)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示