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

题意

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

思路

因为 2 2 2,所以第一个箱子里只能放奇数编号的球。
如果第一个箱子里放 a a a,那么 a ∗ 2 m − 1 ≤ n a*2^{m-1}\leq n a2m1n
那么我们就知道 2 m 2^m 2m里可以放了,然后有
a ∗ 2 x ( x ≡ 0 ( m o d   m ) , a ≡ 1 ( m o d   2 ) , a ∗ 2 x + 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 / 2 m − 1 n/2^{m-1} n/2m1,统计一遍奇数,然后每次让 n / 2 m n/2^m n/2m,统计奇数。
因为 n n n比较大,所以我们需要用高精度。

代码

#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();
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值