ZR1158

ZR1158

http://www.zhengruioi.com/contest/446/problem/1158

给定限制的问题大多数都是容斥或者二分,或者二分之后容斥

首先,这个问题的第一步我们还是比较容易地去转换的,发现每个物品选的次数一定是\(2^i - 1\)次,而下一次我们就是花费\(2^i\)的代价去把答案\(+1\)

\(x\)物品第\(i\)次选择的代价就是\(x\times 2^{i - 1}\),

现在问题变成了一个最大的代价\(cost\)使得所有代价小于等于\(cost\)的物品全部选择之后代价和不超过\(n\)

这个东西就有二分性,所以,不能直接二分答案的题目尝试把问题转化后进行二分,所以,我们接下来问题变成了对于一个给定的代价\(M\),求所有代价小于等于\(M\)的物品的代价之和

之后我们发现所有小于等于\(M\)的代价是长成这个样子的

\[\begin{align} & 1\times 2 ^0 \ \ \ \ \ 2\times2^0 \ \ \ \ \ \dots \ \ \ \ \ M \times 2^0\\\ & 2\times2^1 \ \ \ \ \ 2\times2^1 \ \ \ \ \ \dots \ \ \ \ \ \lfloor\frac{M}{2}\rfloor\times2^1\\ & \dots \end{align} \]

很明显,最多只会有log行,每一行都是一个等差数列

而很明显尾项不能超过\(M\),所以项数就会变成\(\lfloor\frac{M}{x}\rfloor\)

这样我们直接暴力枚举log行,每一行都用等差数列求和算一下总的贡献即可

之后注意

我们设最终二分答案出的答案为\(x\),那么\(x\)一定是\(k\times2^i - 1\)的形式

可能会出现\(k+1\)不能全部放下,但是可以放一部分的情况,这一部分我们可以通过除法最后计算贡献

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#include<set>
#include<map>
#define LL long long
#define pii pair<LL,LL>
#define mk make_pair
#define fi first
#define se second
using namespace std;
int T;LL n;
LL sum;
inline LL check(LL x){
	sum = 0;
	for(LL p = 1;p <= x;p <<= 1) sum += (x / p * p + p) * (x / p) / 2;
	return sum;
}
inline LL geta(LL x){
	LL res = 0;
	for(LL p = 1;p <= x;p <<= 1) res += x / p;
	return res;
}
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%lld",&n);int ans = 0;LL res = 0;
		int l = 1,r = 2000000000;
		while(l <= r){
			int mid = (l + r) >> 1;
			LL now = check(mid);
		//	printf("%d %d %d %lld\n",l,r,mid,now);
			if(now <= n) l = mid + 1,ans = mid,res = now;
			else r = mid - 1;
		}
		printf("%lld\n",geta(ans) + (n - res) / (ans + 1));
	}
	return 0;
}

posted @ 2019-10-25 07:47  wyxdrqcccc  阅读(...)  评论(...编辑  收藏