题解 P1066 [NOIP2006 提高组] 2^k进制数

题面
常规思路:递推

\(first\)

先找思路,看到样例分析中的计算过程感觉有点像等差数列求和,再自己手操几个小案例,发现又有点像等差数列的和再求和,再往后发现貌似又像和求和的求和……

于是感觉到了一点规律性的东西:可以递推.

\(second\)

仔细分析样例计算过程,当第二位数(\(2^k\)进制)确定时,其可能数是确定的;当第三位数确定时,第二位数不确定,但有范围,可以累加求和;于是可以递推下去,当第四位数确定时,可以得到第三位数的范围,再将第三位数的状态结果求和,得到答案.

\(third\)

设置状态方程:\(f_{i,j}\)表示第\(i\)位数字为\(j\)\(i\)\(2^k\)进制数的可能数.
为了后续递推方便,顺便做如下定义:\(~~~~ g_i\)为第\(i\)位数字的最大值,\(t=\left\lfloor\dfrac{w}{k}\right\rfloor,~~~~ r=w\%k,~~~~ p=2^r-1\).

显然有起始状态:\(f_{1,j}=1,~~ g_i=2^k-i\)

转移方程:\(f_{i+1,j}=\sum_{\theta=j+1}^{g_i}f_{i,\theta}\)

目标:

  • \(k\mid w,\text{则}ans=\sum_{i=2}^{t}\sum_{j=1}^{g_i}f_{i,j}\)
  • \(k\nmid w,\text{则}ans=\sum_{i=2}^{t}\sum_{j=1}^{g_i}f_{i,j}+\sum_{j=1}^{\min{p,g_{t+1}}}f_{t+1,j}\)

\(forth\)

细节问题:由于空间问题,\(f_{i,j}\)可用滚动数组压去一维,即时记录即可.

\(fifth\)

代码:

#include<bits/stdc++.h>
using namespace std;

const int digit=200;
const int num=1<<9;
const int xx=3e4;

struct node
{
	int a[digit+1];
}f[2][num+1],ans,zero;

int g[xx+1],k,w;

node operator + (node t1,node t2)
{
	int x=max(t1.a[0],t2.a[0]);
	node res=zero;
	for(int i=1;i<=x;++i)
	{
		res.a[i]+=t1.a[i];
		res.a[i]+=t2.a[i];
		if(res.a[i]>=10)
			res.a[i]%=10,++res.a[i+1];
	}
	if(res.a[x+1])
		++x;
	res.a[0]=x;
	return res;
}

int main()
{
	memset(zero.a,0,sizeof(zero.a));
	scanf("%d%d",&k,&w);
	int t=w/k,r=w%k;
	int p=(1<<r)-1;
	if(r)
		++t;
	for(int i=1;i<=t;++i)
		g[i]=(1<<k)-i;
	for(int j=1;j<=g[1];++j)
	{
		f[0][j]=f[1][j]=zero;
		f[1][j].a[0]=f[1][j].a[1]=1;
	}
	for(int i=2;i<t;++i)
		for(int j=g[i];j>=1;--j)
		{
			f[i&1][j]=f[i&1][j+1]+f[(i-1)&1][j+1];
			f[(i-1)&1][j+1]=zero;
			ans=ans+f[i&1][j];
		}
	for(int j=g[t];j>=1;--j)
		f[t&1][j]=f[t&1][j+1]+f[(t-1)&1][j+1];
	if(r)
		for(int j=min(p,g[t]);j>=1;--j)
			ans=ans+f[t&1][j];
	else
		for(int j=g[t];j>=1;--j)
			ans=ans+f[t&1][j];
	for(int i=ans.a[0];i>=1;--i)
		printf("%d",ans.a[i]);
	printf("\n");
	return 0;
}

完结撒花\(QwQ\)

posted @ 2021-07-20 13:33  wyd_forever  阅读(239)  评论(0)    收藏  举报