jzoj3771-小Z的烦恼【高精度,数学】

版权声明:原创,未经作者允许禁止转载 https://blog.csdn.net/Mr_wuyongcong/article/details/86553496

正题


题目大意

m m 个盒子,
当第 i i 个盒子中放了 x x ,那么 i + 1 i+1 个盒子中就必须放 2 x 2x i < = m i<=m )。
1 n 1\sim n m m 个盒子,求第一个盒子中可以放多少个


解题思路

1号盒子中的肯定越小越好。
要求满足条件那么首先 x 2 m 1 n x*2^{m-1}\leq n
那么 x n / 2 m 1 x\leq n/2^{m-1}
然后奇数是肯定可以放进去的,
然后对于每个
x x ,会封锁 x 2 i ( i < m ) x*2^i(i<m)
之和我们会发现 x 2 m x*2^m 又可以放进去了。
以此类推
我们可以发现 x 2 k m ( x % 2 = = 1 , k 0 ) x*2^{km}(x\%2==1,k\geq0)
所以我们考虑枚举 k k
开始先计算最大的 x x ,统计一次奇数个数。
其实枚举 k k ,就是每次让 n n 除与 2 m 2^m ,然后统计一遍奇数个数。


c o d e code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll mod=1e11;
const ll W=1000;
ll a[W+10],ans[W+10],m,t,l,al;
char s[W*10+10];
void print(ll x){
	if (x>9) print(x/10); putchar(x%10+48); return;
}
void init()
{
	memset(s,0,sizeof(s));
	scanf("%s",s+1);
	scanf("%lld",&m);
	l=strlen(s+1);ll t=0,k=1;
	for(ll i=l;i>=1;i--)
	{
		a[t]+=(s[i]-48)*k;k*=10;
		if(k==mod) t++,k=1;
	}
	l=W;al=0;
}
void div(ll x)
{
	ll g=0;
	for(ll i=l;i>=0;i--)
	{
		a[i]+=g*mod;
		g=a[i]%x;
		a[i]/=x;
	}
	while(!a[l]&&l>=0) 
	  l--;
}
void count_odd(){
	ll g=0,i;
	ans[0]+=a[0]%2;
	for(i=l;i>=0;i--){
		ans[i]+=(a[i]+g*mod)/2;
		g=a[i]%2;
	}
	ll t=max(l-1,al);
	if(ans[t+1]) t++;
	for(i=0;i<=t;i++)
	  if(ans[i]>=mod)
	  {
		  ans[i+1]++;
		  ans[i]-=mod;
	  }
	if(ans[i]>0) al=i;
	else al=i-1;
}
void write()
{
	print(ans[al]);
	while(al--){
		if(ans[al]<1e10) putchar(48);
		if(ans[al]<1e9) putchar(48);
		if(ans[al]<1e8) putchar(48);
		if(ans[al]<1e7) putchar(48);
		if(ans[al]<1e6) putchar(48);
		if(ans[al]<1e5) putchar(48);
		if(ans[al]<1e4) putchar(48);
		if(ans[al]<1e3) putchar(48);
		if(ans[al]<1e2) putchar(48);
		print(ans[al]);
	}
	putchar('\n');
}
int main()
{
	scanf("%lld",&t);
	while(t--){
		memset(a,0,sizeof(a));
		memset(ans,0,sizeof(ans));
		init();
		div(1<<(m-1));
		count_odd();
		while(a[0]){
			div(1<<m);
			count_odd();
		}
		write();
	}
}

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/86553496