【JZOJ A组】小 Z 的烦恼

Description

小 Z 最近遇上了大麻烦,他的数学分析挂科了。于是他只好找数分老师求情。

善良的数分老师答应不挂他,但是要求小 Z 帮助他一起解决一个难题问题是这样的,现在有 n 个标号为 1~n 的球和 m 个盒子,每个球都可以放进且只能放进一个盒子里面,但是要满足如下的规则:

  1. 若把标号为 i 的球放进了第 j 个盒子,那么标号为 2*i 的球一定要在第 j+1 个盒子里面(若 j<m)

  2. 若把标号为 i 的球放进了第 j 个盒子,并且 k*2=i,那么标号为 k 的球一定要在第 j-1 个盒子里面(若 j>1)

小 Z 的数分老师想要知道,给定了 n 和 m 的时候,第一个盒子最多能放进去多少个球。事实上,他已经推算出了公式,但是需要检验当 n 趋向于无穷大时是否仍然满足这个公式,因此 n 可能会非常大。

Input

本题包含多组数据,第一行为一个数(T<=20),表示数据组数;以下 T 行,每组数据一行,包括两个数 n 和 m。

Output

每组数据输出一行,包括一个数,即第一个盒子最多能放进多少个球。

Sample Input

2

10 2

10 3

Sample Output

4

1

Data Constraint

对于 10%的数据,n<=10^6

对于 20%的数据,n<=10^9

对于 30%的数据,m=2

对于 100%的数据,n<=10^10000,2<=m<=25

Hint

样例解释:

(1).{1,3,4,5}, {2,6,8,10}

(2).{1},{2},{4}

思路

首先,能放在第一个盒子的数,满足a∗2x(x≡0(modm),a≡1(mod2),a∗2x+m−1<=n)
所以我们考虑从0开始枚举x,先将n除去2m−1,每次统计1~n里面的奇数,然后将n除去2m即可,由于n很大,所以每次要高精度除法。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
char s[10050];
ll w,j;
int i,m;
struct gj
{
	ll a[677];
	int len;
	void rd()
	{
		scanf("%s",s); int m=strlen(s);len=(m-1)/18+1;
		for(int i=1; i<=len; i++)
			for(ll j=m-i*18; j<m-(i-1)*18; j++)
				if(j>=0) a[i]=a[i]*10+s[j]-'0';
		while(len&&a[len]==0) len--;
	}
	void wr()
	{
		bool zw=false;
		for(int i=len; i>=1; i--)
			for(ll j=w/10; j>0; j/=10)
				if(i<len) putchar(a[i]/j%10+'0');
				else if(a[i]/j%10) putchar(a[i]/j%10+'0'),zw=true;
				else if(zw) putchar(a[i]/j%10+'0');
		if(!len) putchar('0');
	}
	gj operator / (const int &p)const
	{
		gj cll;
		memset(cll.a,0,sizeof(cll.a));
		cll.len=len;
		ll psz=0,gh=w>>p,fdc=w&((1<<p)-1),xhr;
		for(int i=len;i>=1;i--)
		{
			xhr=psz*gh+((a[i]+psz*fdc)>>p);
			psz=(a[i]+psz*fdc)&((1<<p)-1);
			cll.a[i]=xhr;
		}
		while(cll.len && cll.a[cll.len]==0) cll.len--;return cll;
	}
	gj operator + (const gj &p)const
	{
		gj cll; 
		memset(cll.a,0,sizeof(cll.a));
		cll.len=max(len,p.len);
		for(int i=1;i<=cll.len;i++) cll.a[i]=a[i]+p.a[i];
		for(int i=1;i<=cll.len;i++) cll.a[i+1]+=cll.a[i]/w,cll.a[i]%=w;
		if(cll.a[cll.len+1]) cll.len++;
		return cll;
	}
	gj operator - (const gj &p){
		gj cll;
		memset(cll.a,0,sizeof(cll.a));
		cll.len=len;
		for(int i=1;i<=cll.len;i++) cll.a[i]=a[i]-p.a[i];
		for(int i=1;i<=cll.len;i++) if(cll.a[i]<0) cll.a[i]+=w,cll.a[i+1]--;
		while(cll.len && cll.a[cll.len]==0) cll.len--;
		return cll;
	}
}n,l,r,ans;
int main()
{
	int t;
	scanf("%d",&t);
	w=1;
	for(int i=1; i<=18; i++) w*=10;	
	while(t--)
	{
		memset(n.a,0,sizeof(n.a)); memset(ans.a,0,sizeof(ans.a)); ans.len=0;
		n.rd();
		scanf("%d",&m);
		r=n/(m-1); l=n/m;
		while(r.len)
		{
			ans=ans+r-l;
			r=r/m; l=l/m;
		}
		ans.wr();
		printf("\n");
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值