CF1452F Divide Powers 题解

Codeforces
Luogu

Description.

有一个长度为 \(n (n\le 30)\) 的序列,第 \(i\) 个是 \(a_i\)
你每次可以选择一个 \(i\in[2,n]\),使得 \(a_i\leftarrow a_i-1,a_{i-1}\leftarrow a_{i-1}+2\)
问使得 \(\sum_{i=1}^xa_i\ge K\) 的最小操作次数。

Solution.

考虑分类,\(i\le x\)\(i>x\)
如果 \(i\le x\),那每次可以花一步代价使得答案加一。
如果 \(i>x\),那每次可以花 \(2^{x-i}-1\) 步使得答案加 \(2^{x-i}\)
长远考虑肯定是第二种操作更优,且第二种操作中肯定 \(x\) 越小效率越高。
所以从 \(x+1\) 开始能操作满就操作,这样贪心正确性显然。
直到一个地方不能操作满了,那就结束。
接下来,相当于有两种决策,且没有完全最优性。
可以考虑第一种操作枚举到哪里了。
考虑一个 \(2^n\) 每次拆成两个,如果减地动肯定要贪心减。
然后每次直接取最小值即可。
然后直接 \(O(n)\) 扫描即可。

Coding.

点击查看局若代码
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
	x=0;char c=getchar(),f=0;
	for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
	for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}//}}}
int n,Q,cn[35];
int main()
{
	read(n,Q);for(int i=0;i<n;i++) read(cn[i]);
	for(int fg,x;Q--;)
	{
		ll k;read(fg,x,k);if(fg&1) {cn[x]=k;continue;}
		int sm=0;ll rs=0;for(int i=0;i<=x;i++) sm+=cn[i];
		if(sm>=k) {printf("%d\n",0);continue;}else k-=sm;
		ll tt=0;for(int i=1;i<=x;i++) tt+=((1ll<<i)-1)*cn[i];
		ll wj=0;for(int i=0;i<n;i++) wj+=(1ll<<i)*cn[i];
		if(wj<k) {puts("-1");continue;}
		int jc=0;for(int i=x+1;i<n;i++)
		{
			int lf=min(1ll*cn[i],k>>(i-x));k-=(1ll<<(i-x))*lf;
			rs+=1ll*lf*((1ll<<(i-x))-1),tt+=((1ll<<i)-(1ll<<(i-x)))*lf;
			if(lf<cn[i]) {jc=i;break;}
		}if(k==0) {printf("%lld\n",rs);continue;}
		if(jc==0) {printf("%lld\n",tt>=k?rs+k:-1ll);continue;}
		ll as=1e18;for(int i=jc;i>x;)
		{
			if(tt>=k) as=min(as,rs+k);
			rs++,i--;if(k>=(1ll<<(i-x)))
				rs+=(1ll<<(i-x))-1,k-=1ll<<(i-x),tt+=((1ll<<i)-(1ll<<(i-x)));
		}printf("%lld\n",min(as,rs));
	}
	return 0;
}
posted @ 2021-09-13 22:09  Peal_Frog  阅读(33)  评论(0编辑  收藏  举报