ABC 216 E.Amusement Park (数学+思维)

72 篇文章 4 订阅

链接

题意

给出你 n n n个数, m m m次操作(可以不操作到m次防止出现负数),每次操作后将其加到 a n s ans ans中去,然后对这个数进行-1操作,问最大 a n s ans ans为多少.

分析

首先我们贪心想,肯定是先对大的数进行操作,那么我们对数组进行排序一次。这样我们直接从大的开始操作即可。

然后我们看 a [ n ] a[n] a[n] a [ n − 1 ] a[n-1] a[n1]我们看看他们直接的差 ( x = a [ n ] − a [ n − 1 ] ) (x=a[n]-a[n-1]) (x=a[n]a[n1])如果 x < = k x<=k x<=k 那么证明我们能直接从 a [ n ] a[n] a[n]操作到 a [ n − 1 ] a[n-1] a[n1].贡献是 a [ n ] + a [ n − 1 ] + . . . + ( a [ n − 1 ] + 1 ) a[n]+a[n-1]+...+(a[n-1]+1) a[n]+a[n1]+...+(a[n1]+1)。代价是 ( x ) (x) (x).

然后我们递推到普遍情况:
a [ i ] a[i] a[i] a [ i − 1 ] a[i-1] a[i1]这样一共有 n − i + 1 n-i+1 ni+1个a[i],要将其都变成 a [ i − 1 ] a[i-1] a[i1]花费的代价是 ( n − i + 1 ) ∗ x (n-i+1)*x (ni+1)x, ( x = a [ i ] − a [ i − 1 ] ) (x=a[i]-a[i-1]) (x=a[i]a[i1]),贡献是 n − i + 1 n-i+1 ni+1 ( a [ i ] + a [ i ] − 1 + . . . + ( a [ i − 1 ] + 1 ) ) (a[i]+a[i]-1+...+(a[i-1]+1)) (a[i]+a[i]1+...+(a[i1]+1))这个我们可以用等差序列的前n项和来解决。
那退出条件那,也就是边界值,分为两种:

  • 都变成0了,我们不能加负数(这样会让其减少)
  • 如果k<x,这样我们就不能将 ( n − i + 1 ) (n-i+1) (ni+1) a [ i ] a[i] a[i]全部转化成 a [ i − 1 ] a[i-1] a[i1],那么我们选优肯定是,先对大数进行操作,那么我们用 l = k / ( n − i + 1 ) l=k/(n-i+1) l=k/(ni+1)这是我们可以整体移动的次数, r = k % ( n − i + 1 ) r=k\%(n-i+1) r=k%(ni+1)这是单独的操作.贡献计算方法是这样的 整体移动我们可以直接用前缀和来维护,单独操作,我们是对 a [ i ] − l a[i]-l a[i]l操作 r r r次,贡献就是 r ∗ ( a [ i ] − l ) r*(a[i]-l) r(a[i]l)这样就是全部贡献。
// Problem: E - Amusement Park
// Contest: AtCoder - AtCoder Beginner Contest 216
// URL: https://atcoder.jp/contests/abc216/tasks/abc216_e
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef unsigned long long ull;
 
#define x first
#define y second
#define sf scanf
#define pf printf
#define PI acos(-1)
#define inf 0x3f3f3f3f
#define lowbit(x) ((-x)&x)
#define mem(a,x) memset(a,x,sizeof(a))
#define rep(i,n) for(int i=0;i<(n);++i)
#define repi(i,a,b) for(int i=int(a);i<=(b);++i)
#define repr(i,b,a) for(int i=int(b);i>=(a);--i)
#define debug(x) cout << #x << ": " << x << endl;
 
const int MOD = 998244353;
const int mod = 1e9 + 7;
const int maxn = 2e5 + 10;	
const int dx[] = {0, 1, -1, 0, 0};
const int dy[] = {0, 0, 0, 1, -1};
const int dz[] = {1, -1, 0, 0, 0, 0 };
int day[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};


void init(){
	
}
ll n,k;
ll a[maxn];
void solve()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);		
	}
	sort(a+1,a+1+n);
	ll ans=0;
	a[0]=0;
	for(int i=n;i>=1;i--){
		ll cha=(a[i]-a[i-1]);
		if(cha){
			ll num=(n-i+1);
			if(k>=num*cha){
				k-=num*cha;						
				ans+=num*(cha*(a[i-1]+1)+(cha-1)*(cha)/2);
				//cout<<ans<<endl;			
			}else {
				ll l=k/num;
				ll r=k%num;
				ans+=num*(l*a[i]-(l-1)*(l)/2);
				ans+=r*(a[i]-l);
				break;
			}
		}
	}
	cout<<ans<<endl;
}
 
int main()
{
	init();
	ll t = 1;
    //scanf("%lld",&t);
    while(t--)
    {
        solve();
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值