【数学 高精度】JZOJ_3771 小 Z 的烦恼

探讨了一个关于将球放入盒子的数学问题,利用高精度计算处理大数值,确保奇数编号球的放置遵循特定规则,同时介绍了实现这一算法的C++代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

nnn个球要放到mmm个箱子里,有以下两条规则:
1、若把标号为iii的球放进了第jjj个盒子,那么标号为2∗i2*i2i的球一定要在第j+1j+1j+1个盒子里面(((j&lt;m)j&lt;m)j<m)
2、若把标号为iii的球放进了第jjj个盒子,并且k∗2=ik*2=ik2=i,那么标号为kkk的球一定要在第j−1j-1j1个盒子里面(((j&gt;1)j&gt;1)j>1)
求第一个箱子里最多可以放多少球。

思路

因为222,所以第一个箱子里只能放奇数编号的球。
如果第一个箱子里放aaa,那么a∗2m−1≤na*2^{m-1}\leq na2m1n
那么我们就知道2m2^m2m里可以放了,然后有
a∗2x(x≡0(mod m),a≡1(mod 2),a∗2x+m−1&lt;=n)a∗2^x(x≡0(mod\ m),a≡1(mod\ 2),a∗2^{x+m−1}&lt;=n)a2x(x0(mod m),a1(mod 2),a2x+m1<=n)
所以我们每次让n/2m−1n/2^{m-1}n/2m1,统计一遍奇数,然后每次让n/2mn/2^mn/2m,统计奇数。
因为nnn比较大,所以我们需要用高精度。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>

const long long P = 1e11;
const int MAXN = 1000;
int t, m, l, al;
long long a[MAXN + 1], ans[MAXN + 1];
char s[10001];

void init() {
	memset(s, 0, sizeof(s));
	memset(ans, 0, sizeof(ans));
	scanf("%s", s + 1);
	scanf("%d", &m);
	l = strlen(s + 1);
	int g = 0;
	long long k = 1;
	for (int i = l; i >= 1; i--) {
		a[g] += (s[i] - 48) * k;
		k *= 10;
		if (k == P) k = 1, g++;
	}
	l = MAXN;
	al = 0;
}

void div(long long d) {
	long long g = 0;
	for (int i = l; i >= 0; i--) {
		a[i] += g * P;
		g = a[i] % d;
		a[i] /= d;
	}
	while (!a[l] && l)
		l--;
}

void countAns() {
	long long g = 0;
	ans[0] += a[0] % 2;
	for (int i = l; i >= 0; i--) {
		ans[i] += (a[i] + g * P) / 2;
		g = a[i] % 2;
	}
	int t = std::max(l - 1, al);
	if (ans[t + 1]) t++;
	for (int i = 0; i <= t; i++)
		if (ans[i] >= P) {
			ans[i] -= P;
			ans[i + 1]++;
		}
	if (ans[t + 1]) al = t + 1;
	else al = t;
}

void print() {
	printf("%lld", ans[al]);
	while (al--) {
		if (ans[al] < 1e10) printf("0");
		if (ans[al] < 1e9) printf("0");
		if (ans[al] < 1e8) printf("0");
		if (ans[al] < 1e7) printf("0");
		if (ans[al] < 1e6) printf("0");
		if (ans[al] < 1e5) printf("0");
		if (ans[al] < 1e4) printf("0");
		if (ans[al] < 1e3) printf("0");
		if (ans[al] < 1e2) printf("0");
		printf("%lld", ans[al]);
	}
	printf("\n");
}

int main() {
	scanf("%d", &t);
	for (; t; t--) {
		init();
		div(1 << m - 1);
		countAns();
		while (a[0]) {
			div(1 << m);
			countAns();
		}
		print();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值