一、题目
二、解法
我们考虑用线性基表示
,集合
还需要一些线性基组合出来的,但是不在线性基内的数,我们就保证线性基的最大值在
以内。但是还要保证映射的唯一性,我们把线性基消除成上三角形式,就是如果一个基的最高位有值,那么其他基就必须
,举个例子(
小技巧:二级对齐\begin{alignedat}{2}\end{alignedat}
):
其中
可以随便填,这样就可以保证一一对应,从两方面说明:如果基位置不同或者基个数不同,那么集合肯定不同,如果
不同那么集合也不同。这样就说明了是唯一映射。
那么问题变成了求满足上述条件的线性基个数,可以考虑二进制位 ,设 表示从高到低位,现在到了 这一位, 为基的个数, 表示 是否顶到上界,满足条件的线性基个数,来讨论一波转移。
Case one:k=0
- 不新加入基,而是修改 (含义见上图),然后我们随便怎么填都能满足条件,因为当前的 已经为 了,转移:
- 新加入基,转移:
Case two:k=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);
}