题解 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\)

浙公网安备 33010602011771号