CF1425F Divide Powers 构造乱搞

题目大意:
给你一个由 2 n 2^n 2n组成的集合, 2 i 2^i 2i c n t i cnt_i cnti个。
i i i不为0时,我们可以使用一次操作把 2 i 2^i 2i变成两个 2 i − 1 2^{i-1} 2i1
有2类操作,修改 c n t i cnt_i cnti,或者询问至少要多少次操作后有不少于 k k k个元素小于等于 2 x 2^x 2x
n < = 30 , q < = 2 e 5 n<=30,q<=2e5 n<=30,q<=2e5

分析:
对于小于等于 2 x 2^x 2x的元素,进行一次操作可以增加一个元素;而对于大于 2 x 2^x 2x的元素,假设为 2 l 2^l 2l,进行 2 l − x − 1 2^{l-x}-1 2lx1次操作可以得到 2 l − x 2^{l-x} 2lx个元素。所以我们先考虑把处理大于 2 x 2^x 2x的元素。

假设我们不分解小于等于 2 x 2^x 2x的元素,那么处理出 d d d 2 x 2^x 2x时,我们按任意顺序分解相同的几个大于 2 x 2^x 2x的元素,需要的操作数时相同的。

那么我们可以从小到大处理大于 2 x 2^x 2x的元素,记录下还需要多少个元素 k k k,以及把所有已选元素分解到 2 0 2^0 20还能产生多少元素 l i m lim lim。因为分解 2 l 2^l 2l可以产生 2 l − x 2^{l-x} 2lx个元素,如果 2 l − x < = k 2^{l-x}<=k 2lx<=k,那么直接分解即可;否则先判断 l i m lim lim是否大于等于 k k k,因为不把 2 l 2^l 2l完全分解成 2 x 2^x 2x,那么就没有分解小与等于 2 x 2^x 2x的优。如果 l i m < k lim<k lim<k,考虑用一个操作把它分解成两个 2 l − 1 2^{l-1} 2l1继续处理即可。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
 
const int maxn=35;
 
using namespace std;
 
int n,m,op,x;
LL k,lim,ans;
LL a[maxn],bit[maxn];
 
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=0;i<n;i++) scanf("%lld",&a[i]);
	bit[0]=1;
	for (int i=1;i<=n;i++) bit[i]=bit[i-1]*2;	
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d%lld",&op,&x,&k);
		if (op==1) a[x]=k;
		else
		{
			
			lim=ans=0;
			for (int i=0;i<=x;i++)
			{
				k-=a[i];
				lim+=a[i]*(bit[i]-1);
			}
			if (k<=0)
			{
				printf("0\n");
				continue;
			}
			for (int i=x+1;i<n;i++)
			{
				if (k<a[i]*bit[i-x])
				{
					int p=k/bit[i-x];
					k%=bit[i-x];
					lim+=p*(bit[i]-bit[i-x]);
					ans+=p*(bit[i-x]-1);
					int j=i-1;
					for (int j=i;j>=x;j--)
					{
						if (k<=lim)
						{
							ans+=k;
							k=0;
							break;
						} 
						if (bit[j-x-1]<=k)
						{
							k-=bit[j-x-1];
							lim+=bit[j-1]-bit[j-x-1];
							ans+=bit[j-x-1];
						}
						else ans++;
					}
					break;
				}
				else
				{
					k-=a[i]*bit[i-x];
				    lim+=a[i]*(bit[i]-bit[i-x]);
					ans+=a[i]*(bit[i-x]-1);
				}
			}			
			if (k<=lim) printf("%lld\n",ans+k);
			       else printf("-1\n");
		}
	}
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值