【NOI2015模拟8.15】小 Z 的烦恼

这题有点卡常的味道。。。
正解找规律+高精度。
经过手推后发现样例:
10 2↙
1 2 4 8
3 6
5 10
由于m=2,所以每两个一组,共4组。
哇,这就是规律。
第一次将n除以2m-1,而后找到1~n中奇数的个数并添加到ans。
之后每次都将n除以2m,而后找到1~n中奇数的个数并添加到ans。
最后输出即可。
上标(我压了8位™都还跑了2069ms,重点是我用了很多优化啊(没有吸氧) ):

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define mo 100000000
#define rg register int
using namespace std;
ll a[1310],ans[1310],b[1310];
char c,s[10030];
int T,m,k;

void divide(int div)
{
	ll x=0;
	for (rg i=a[0];i>0;i--)
		a[i]=a[i]+x*mo,x=a[i]%div,a[i]/=div;
	while (!a[a[0]] && a[0]>0) a[0]--;
}

void inans()
{
	b[0]=a[0];
	ll x=0;
	for (rg i=b[0];i>0;i--)
		b[i]=a[i]+x*mo,x=b[i] & 1,b[i]>>=1;
	while (!b[b[0]] && b[0]>0) b[0]--;
	ans[0]=max(ans[0],b[0]);
	x=0;
	for (rg i=1;i<=b[0];i++)
		ans[i]+=b[i]+x,x=ans[i]/mo,ans[i]%=mo;
	if (x) ans[0]=max(ans[0],b[0]+1),ans[b[0]+1]+=x;
}

int main()
{
	freopen("upset.in","r",stdin);
	freopen("upset.out","w",stdout);
	scanf("%d\n",&T);
	while (T--)
	{
		scanf("%s %d",s+1,&m);a[0]=0;
		memset(ans,0,sizeof(ans));ans[0]=1;
		for (rg i=strlen(s+1);i>0;i-=8)
		{
			a[++a[0]]=0;
			for (rg j=max(i-7,1);j<=i;j++)
				a[a[0]]=(a[a[0]]<<1)+(a[a[0]]<<3)+(s[j]^48);
		}
		divide(1<<m-1);
		inans();
		if (a[1] & 1)
		{
			ans[k=1]++;
			while (ans[k]==mo) ans[k]=0,ans[++k]++;
		}
		while (a[0])
		{
			divide(1<<m);
 			inans();
			if (a[1] & 1)
			{
				ans[k=1]++;
				while (ans[k]==mo) ans[k]=0,ans[++k]++;
			}
		}
		printf("%lld",ans[ans[0]]);
		for (rg i=ans[0]-1;i>0;i--)
			printf("%08lld",ans[i]);
		puts("");
	}
	return 0;
}
posted @ 2019-01-19 16:27  jz929  阅读(143)  评论(0编辑  收藏  举报