……

解题报告:luogu P2558

题目链接:P2558 [AHOI2002]网络传输
\(dp\) 入门题,结果调了一晚上,枯了。
很显然 \(x^{n+1}>\sum\limits_{i=0}^n x^n\),那么我们对于每个次方数,加上他前面的任意数的组合一定小于下一个次方数。
容易得到:

\[f_{i}=f_{2^{\left\lfloor\log_{2}i\right\rfloor}}+f_{i-2^{\left\lfloor\log_{2}i\right\rfloor}} \]

可以递推实现。
然而我们发现最后的结果小于 \(2^{50}\) ,高精好麻烦啊!
鉴于 \(50\) 比较小,我们尝试压位。
把一个数定义成结构体形式:

struct num
{
	ll a,b,c,d;
};

考虑到 \(\left\lceil\dfrac{50}{4}\right\rceil=13\)long long范围完全可以接受,于是把一个数分成四块。
然后定义运算就好了:
加法:

num add(num x,num y)
{
	num z;
	z.a=z.b=z.c=0;
	z.a=y.a+x.a;
	z.b=y.b+x.b;
	z.c=y.c+x.c;
	z.d=y.d+x.d;
	return arrg(z);
}

这里的arrg函数是把不符合一块内最多 \(13\) 位的数整理成符合条件的数。
可以这样写:

#define MOD 10000000000000
num arrg(num x)
{
	num z=x;
	if(z.a>=MOD) z.b+=z.a/MOD,z.a%=MOD;
	if(z.b>=MOD) z.c+=z.b/MOD,z.b%=MOD;
	if(z.c>=MOD) z.d+=z.c/MOD,z.c%=MOD;
	return z;
}

然后还有普通数乘分块数:

num mul(num x,ll k)
{
	num z;
	z.a=z.b=z.c=0;
	z.a=x.a*k;
	z.b=x.b*k;
	z.c=x.c*k;
	z.d=x.d*k;
	return arrg(z);
}

这样,我们就减少了原要写高精的码量(bushi,输出打的我焦头烂额
时间复杂度为 \(\mathcal O(p)\),可以通过本题。
完整代码如下:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;

#define ll long long  
#define MOD 10000000000000

struct num
{
	ll a,b,c,d;
	//压13位 
}ans[1030],mi[20];
int n,p,cnt=0;

num arrg(num x)
{
	num z=x;
	if(z.a>=MOD) z.b+=z.a/MOD,z.a%=MOD;
	if(z.b>=MOD) z.c+=z.b/MOD,z.b%=MOD;
	if(z.c>=MOD) z.d+=z.c/MOD,z.c%=MOD;
	return z;
}

num add(num x,num y)
{
	num z;
	z.a=z.b=z.c=0;
	z.a=y.a+x.a;
	z.b=y.b+x.b;
	z.c=y.c+x.c;
	z.d=y.d+x.d;
	return arrg(z);
}

num mul(num x,ll k)
{
	num z;
	z.a=z.b=z.c=0;
	z.a=x.a*k;
	z.b=x.b*k;
	z.c=x.c*k;
	z.d=x.d*k;
	return arrg(z);
}

int logx(int a,int b){return ceil(log(b)/log(a));}
ll _pow(ll a,ll b){ll ans=1;for(int i=1;i<=b;i++) ans*=a;return ans;}


int main()
{
	scanf("%d%d",&n,&p);
	mi[0].a=1ll,mi[0].b=0,mi[0].c=0,mi[0].d=0;
	ans[++cnt]=mi[0];
	for(int i=1;i<=logx(2,p)+1;i++) mi[i]=mul(mi[i-1],(ll)n);
	if(p==1){printf("1\n");return 0;}
	int k=1;
	for(;;)
	{
		ans[++cnt]=mi[k++];
		if(cnt>=p) break;
		int now=cnt-1;
		for(int j=1;j<=now&&cnt<=p;j++) ans[++cnt]=add(ans[now+1],ans[j]);
		if(cnt>=p) break;
	}
	ans[p]=arrg(ans[p]);
	if(ans[p].d!=0)
	{
		printf("%lld",ans[p].d);
		for(int i=12;i>=0;i--)
		{
			ll g=_pow((ll)10,i);
			printf("%d",(int)(ans[p].c/g));
			ans[p].c%=g;
		}
		for(int i=12;i>=0;i--)
		{
			ll g=_pow((ll)10,i);
			printf("%d",(int)(ans[p].b/g));
			ans[p].b%=g;
		}
		for(int i=12;i>=0;i--)
		{
			ll g=_pow((ll)10,i);
			printf("%d",(int)(ans[p].a/g));
			ans[cnt].a%=g;
		}
	} 
	else if(ans[p].c!=0)
	{
		printf("%lld",ans[p].c);
		for(int i=12;i>=0;i--)
		{
			ll g=_pow((ll)10,i);
			printf("%d",(int)(ans[p].b/g));
			ans[p].b%=g;
		}
		for(int i=12;i>=0;i--)
		{
			ll g=_pow((ll)10,i);
			printf("%d",(int)(ans[p].a/g));
			ans[p].a%=g;
		} 
	}
	else if(ans[p].b!=0)
	{
		printf("%lld",ans[p].b);
		for(int i=12;i>=0;i--)
		{
			ll g=_pow((ll)10,i);
			printf("%d",(int)(ans[p].a/g));
			ans[p].a%=g;
		}
	}
	else printf("%lld",ans[p].a);
	putchar('\n');
	return 0;
}

开始我尝试把一个数分成 \(3\) 块,然而爆long long了,只好分成 \(4\) 块。

posted @ 2020-04-15 23:11  童话镇里的星河  阅读(196)  评论(0编辑  收藏  举报