【JZOJ3771】【NOI2015模拟8.15】小 Z 的烦恼【高精度】【数论】

题目大意:

题目链接:https://jzoj.net/senior/#main/show/3771
ZZ最近遇上了大麻烦,他的数学分析挂科了。于是他只好找数分老师求情。
善良的数分老师答应不挂他,但是要求小ZZ帮助他一起解决一个难题问题是这样的,现在有
nn个标号为 1n1\sim n 的球和mm个盒子,每个球都可以放进且只能放进一个盒子里面,但是要满足如下的规则:

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

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

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


思路:

若第一个盒子放xx,那么接下来的盒子就分别放2x,22x...2m1x2x,2^2x...2^{m-1}x个球。
那么若可以放置,就必须使2m1xn2^{m-1}x\leq n,所以xn÷2m1x\leq n\div 2^{m-1}
很明显,第一个格子放奇数个球是成立的。因为2x2x无法得到奇数。那么ansans就加上n\leq n的所有奇数个数。
那么还有一部分偶数也是可以放置在第一个格子的。所以只需继续让n÷2mn\div 2^m(注意不是2m12^{m-1}),再统计奇数个数。直到n=0n=0为止。
需卡常。


代码:

/*#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")*/
//以上卡常神器
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;

const int MAXN=1120;
const int maxint=1000000000;
int T,len,k;
char ch[10010];
ll n[MAXN+1],ans[MAXN+1],m,t,s;

ll write(ll x)
{
	if (x>9) write(x/10);
	putchar(x%10+48);
}

bool check(ll a[])  //判断n是否为0
{
	for (register int i=MAXN;i>=1;i--)
		if (a[i]) return 0;
	return 1;
}

ll cnt()  //计算奇数个数
{
	//不超过n的奇数个数就是[(n+1)/2],[]表示向下取整
	ll a[MAXN+1];
	memcpy(a,n,sizeof(a));
	t=1;
	for (register int i=MAXN;i>=len-3;i--)  //len用来卡常
	{  //n+1
		if (!t) break;
		a[i]+=t;
		t=a[i]/maxint;
		a[i]%=maxint;
		if (i<len&&a[i]) len=i;
	}
	t=0;
	for (register int i=len;i<=MAXN;i++)  //除以2
	{
		a[i]+=t;
		if (a[i]&1) t=maxint;
			else t=0;
		a[i]>>=1;
		if (i==len&&!a[i]) len++;
	}
	t=0;
	for (register int i=MAXN;i>=len-3;i--)  //记录答案
	{
		ans[i]+=a[i]+t;
		t=0;
		while (ans[i]>=maxint)
		{
			t++;
			ans[i]-=maxint;
		}
		if (i<len&&a[i]) len=i;
	}
}

void div(ll a[],int x)
{
	t=0;
	for (register int i=len-3;i<=MAXN;i++)
	{
		t=t*maxint+a[i];
		a[i]=t/x;
		t%=x;
		if (i==len&&!a[i]) len++;
	}
}

int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%s",ch);
		len=strlen(ch);
		for (register int i=1;i<=len;i++)
			n[MAXN-(len-i)/9]=n[MAXN-(len-i)/9]*10+ch[i-1]-48;
			
		for (int i=1;i<=MAXN;i++)  //人才卡常法
			if (n[i])
			{
				len=i-1;
				break;
			}
		
		scanf("%lld",&m);
		div(n,1<<(m-1));
		
		if (check(n))
		{
			putchar(48),putchar(10);
			continue;
		}
		
		while (!check(n))
		{
			cnt();
			div(n,(1<<m));
		}
		
		k=1;
		while (!ans[k]) k++;
		write(ans[k]);
		ans[k]=0;
		for (k++;k<=MAXN;k++)
		{
			if (ans[k]<10) putchar(48);
			if (ans[k]<100) putchar(48);
			if (ans[k]<1000) putchar(48);
			if (ans[k]<10000) putchar(48);
			if (ans[k]<100000) putchar(48);
			if (ans[k]<1000000) putchar(48);
			if (ans[k]<10000000) putchar(48);
			if (ans[k]<100000000) putchar(48);
			write(ans[k]);
			ans[k]=0;
		}
		putchar(10);
	}
	return 0;
}
posted @ 2019-01-19 20:28  全OI最菜  阅读(110)  评论(0编辑  收藏  举报