Link.
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;
}