P10412 「QFOI R2」钟声远带斜阳 ——思维转化

「QFOI R2」钟声远带斜阳

题目描述

注意:本题中的所有数列下标从 0 开始。

小 R 是一个可爱的女孩子,她喜欢研究无穷数列。

她称一个无穷数列 b 是美妙的,当且仅当存在自然数 k0,使得对于所有 kk0,都满足 b 中下标在区间 [k0,k] 内的所有数的和非负(即 i=k0kbi0)。例如,数列 αi=i5 是美妙的,取 k0=5 符合要求;但 βi=i 不是美妙的。

她目前只有一个长度为 n 的有穷数列 a,可以进行任意次以下三种操作:

  1. 花费 p 的代价,选择一个整数 i0i<n),将 ai 增加一。
  2. 花费 q 的代价,选择一个整数 i0i<n),将 ai 删除,同时更新 n 为新的数列长度。不能将数列删空。
  3. 花费 r 的代价,选择两个整数 i,j0i<j<n),交换 aiaj

她希望在若干次操作后,用无限个有穷数列 a 依次相接得到无穷数列 b(即 bi=aimodn),使得 b 是美妙的。请你求出最小的代价。

输入格式

第一行四个整数 n,p,q,r

第二行 n 个整数,表示数列 a

输出格式

一行,一个整数,表示最小代价。

样例 #1

样例输入 #1

5 1 2 5
2 -2 3 -3 -1

样例输出 #1

1

样例 #2

样例输入 #2

5 2 1 5
2 -2 3 -3 -1

样例输出 #2

1

样例 #3

样例输入 #3

5 1 1 1
0 1 2 3 4

样例输出 #3

0

提示

样例 1 解释

花费 p=1 的代价将 a3 增加一,得到数列 b=[2,2,3,2,1,2,2,3,2,1,] 是美妙的,取 k0=2 符合要求。

可以证明不存在代价更小的方案。


样例 2 解释

花费 q=1 的代价将 a1 删除,得到数列 b=[2,3,3,1,2,3,3,1,] 是美妙的,取 k0=0 符合要求。

可以证明不存在代价更小的方案。


数据范围

本题采用捆绑测试。只有通过子任务中所有测试点以及所有依赖的子任务,才能获得相应的分数。

对于全部数据:1n1051p,q,r109|ai|109

  • 子任务一(10 分):n=1
  • 子任务二(10 分):n10。依赖子任务一。
  • 子任务三(20 分):|ai|1
  • 子任务四(20 分):|ai|105。依赖子任务三。
  • 子任务五(40 分):无特殊限制。依赖子任务一、二、三、四。

分析

转化

转化一下:有一个长为 n 的数字环,从某一个下标沿某个方向循环遍历,遍历过程中累加环上的数字,当且仅当任意时刻数字之和 sum0 称其为美妙的。

因为 bi 是无限长的,所以 S=i=1nai 会不断累加,此时只有 S 的正负性对总和正负性有影响, S<0 必然导致总和从某一位置开始小于零。

条件证明

由上,很容易得到一个符合定义的 ai 的必要不充分条件: S0

以下给出充分性证明:

: 

x0N,1x0n,jN,1jnk=x0x+j1a(k1)%n+10

即存在一个初始位置,使得前 n 步的 n 个和均大于等于零。

不妨从 i 开始,破环为链,以 i 为起点遍历 ai ,对于 x0 有两种情况:

  1. x0kn,j=x0kaj<0 此时找到第一个总和小于零的区间,划分出来,将总和归零从 k 下一位开始走。

  2. x0kn,j=x0kaj0 这个后缀区间是美妙的。

划分区间

可以发现每次得到情况一,左侧和的绝对值均小于等于右侧的绝对值( S0 ),在经过若干次划分后必然存在一个后缀区间的和与左侧所有数之和大于零且该后缀区间是美妙的。从这个下标 h 开始循环遍历形成的 bi 必然是美妙的。

所以操作三是无效的。只需考虑操作一和二,将原数组排序后比较删除和累加哪种更优,注意数列非空即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100;
long long n,p,q,r;
long long g[N],sum,ans;
int main()
{
scanf("%lld%lld%lld%lld",&n,&p,&q,&r);
for(int i=1;i<=n;++i){scanf("%lld",g+i);sum+=g[i];}
sort(g+1,g+1+n);
if(sum>=0){cout<<0;return 0;}
int tot=0;
for(int i=1;i<=n;++i)
{
if(g[i]>=0 || sum>=0){sum=0;break;}
if(q>min(-g[i],-sum)*p)
{
ans+=min(-g[i],-sum)*p;
sum+=min(-sum,-g[i]);
}
else
{
if(tot==n-1)break;
++tot;
ans+=q;
sum-=g[i];
}
}
if(sum<0)
{
if(tot<n-1)
ans+=(-sum*p,q);
else
ans+=-sum*p;
}
cout<<ans;
return 0;
}
posted @   Glowingfire  阅读(22)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示