一、題目
二、解法
我們考慮用線性基表示,集合還需要一些線性基組合出來的,但是不在線性基內的數,我們就保證線性基的最大值在以內。但是還要保證映射的唯一性,我們把線性基消除成上三角形式,就是如果一個基的最高位有值,那麼其他基就必須,舉個例子(小技巧:二級對齊\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);
}