一、题目
二、解法
我们考虑用线性基表示
S
S
S,集合
S
S
S还需要一些线性基组合出来的,但是不在线性基内的数,我们就保证线性基的最大值在
n
n
n以内。但是还要保证映射的唯一性,我们把线性基消除成上三角形式,就是如果一个基的最高位有值,那么其他基就必须
0
0
0,举个例子(
makedown
\text{makedown}
makedown小技巧:二级对齐\begin{alignedat}{2}\end{alignedat}
):
1
x
x
x
0
x
x
x
0
1
x
x
x
0
1
\begin{alignedat}{2} 1xxx&0xxx&0\\ &1xxx&0\\ &&1 \end{alignedat}
1xxx0xxx1xxx001其中
x
x
x可以随便填,这样就可以保证一一对应,从两方面说明:如果基位置不同或者基个数不同,那么集合肯定不同,如果
x
x
x不同那么集合也不同。这样就说明了是唯一映射。
那么问题变成了求满足上述条件的线性基个数,可以考虑二进制位 d p dp dp,设 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示从高到低位,现在到了 i i i这一位, j j j为基的个数, k k k表示 m a x max max是否顶到上界,满足条件的线性基个数,来讨论一波转移。
Case one:k=0
- 不新加入基,而是修改 x x x(含义见上图),然后我们随便怎么填都能满足条件,因为当前的 k k k已经为 0 0 0了,转移: d p [ i − 1 ] [ j ] [ 0 ] = d p [ i ] [ j ] [ 0 ] × 2 j dp[i-1][j][0]=dp[i][j][0]\times 2^j dp[i−1][j][0]=dp[i][j][0]×2j
- 新加入基,转移: d p [ i − 1 ] [ j + 1 ] [ 0 ] = d p [ i ] [ j ] [ 0 ] dp[i-1][j+1][0]=dp[i][j][0] dp[i−1][j+1][0]=dp[i][j][0]
Case two:k=1
如果 n n n在 i − 1 i-1 i−1上有值的话:
- 使最大值 i − 1 i-1 i−1位为 0 0 0,那么: d p [ i − 1 ] [ j ] [ 0 ] = x × d p [ i ] [ j ] [ 1 ] dp[i-1][j][0]=x\times dp[i][j][1] dp[i−1][j][0]=x×dp[i][j][1],其中 x x x是 i − 1 i-1 i−1位为 0 0 0的方案数(如果 j = 0 j=0 j=0那么 x = 1 x=1 x=1,否则 x = 2 j − 1 x=2^{j-1} x=2j−1)
- 使最大值 i − 1 i-1 i−1位为 1 1 1,那么: d p [ i − 1 ] [ j ] [ 1 ] = y × d p [ i ] [ j ] [ 1 ] dp[i-1][j][1]=y\times dp[i][j][1] dp[i−1][j][1]=y×dp[i][j][1],其中 y y y是 i − 1 i-1 i−1位为 1 1 1的方案数(如果 j = 0 j=0 j=0那么 y = 0 y=0 y=0,否则 y = 2 j − 1 y=2^{j-1} y=2j−1)
- 加入一个基: d p [ i − 1 ] [ j + 1 ] [ 1 ] = d p [ i ] [ j ] [ 1 ] dp[i-1][j+1][1]=dp[i][j][1] dp[i−1][j+1][1]=dp[i][j][1]
o t h e r w i s e otherwise otherwise,只能让当前位为 0 0 0: d p [ i − 1 ] [ j ] [ 1 ] = x × d p [ i ] [ j ] [ 1 ] dp[i-1][j][1]=x\times dp[i][j][1] dp[i−1][j][1]=x×dp[i][j][1]
#include <cstdio>
#define int long long
const int jzm = 1e9+7;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,ans,dp[35][35][2];
int add(int &a,int b)
{
a=(a+b)%jzm;
}
signed main()
{
n=read();
dp[30][0][1]=1;
for(int i=30;i>0;i--)
for(int j=0;j<=30;j++)
{
add(dp[i-1][j][0],(1<<j)*dp[i][j][0]%jzm);
add(dp[i-1][j+1][0],dp[i][j][0]);
int x=j?(1<<j-1):1,y=j?(1<<j-1):0;
if(n>>(i-1)&1)
{
add(dp[i-1][j][0],x*dp[i][j][1]%jzm);
add(dp[i-1][j][1],y*dp[i][j][1]%jzm);
add(dp[i-1][j+1][1],dp[i][j][1]);
}
else add(dp[i-1][j][1],x*dp[i][j][1]%jzm);
}
for(int i=0;i<=30;i++)
add(ans,dp[0][i][0]),add(ans,dp[0][i][1]);
printf("%lld\n",ans);
}