CF388D Fox and Perfect Sets

一、题目

点此看题

二、解法

我们考虑用线性基表示 S S ,集合 S S 还需要一些线性基组合出来的,但是不在线性基内的数,我们就保证线性基的最大值在 n n 以内。但是还要保证映射的唯一性,我们把线性基消除成上三角形式,就是如果一个基的最高位有值,那么其他基就必须 0 0 ,举个例子( makedown \text{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} 其中 x x 可以随便填,这样就可以保证一一对应,从两方面说明:如果基位置不同或者基个数不同,那么集合肯定不同,如果 x x 不同那么集合也不同。这样就说明了是唯一映射。

那么问题变成了求满足上述条件的线性基个数,可以考虑二进制位 d p dp ,设 d p [ i ] [ j ] [ k ] dp[i][j][k] 表示从高到低位,现在到了 i i 这一位, j j 为基的个数, k k 表示 m a x max 是否顶到上界,满足条件的线性基个数,来讨论一波转移。

Case one:k=0
  • 不新加入基,而是修改 x x (含义见上图),然后我们随便怎么填都能满足条件,因为当前的 k k 已经为 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
  • 新加入基,转移: d p [ i 1 ] [ j + 1 ] [ 0 ] = d p [ i ] [ j ] [ 0 ] dp[i-1][j+1][0]=dp[i][j][0]
Case two:k=1

如果 n n i 1 i-1 上有值的话:

  • 使最大值 i 1 i-1 位为 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] ,其中 x x i 1 i-1 位为 0 0 的方案数(如果 j = 0 j=0 那么 x = 1 x=1 ,否则 x = 2 j 1 x=2^{j-1}
  • 使最大值 i 1 i-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] ,其中 y y i 1 i-1 位为 1 1 的方案数(如果 j = 0 j=0 那么 y = 0 y=0 ,否则 y = 2 j 1 y=2^{j-1}
  • 加入一个基: d p [ i 1 ] [ j + 1 ] [ 1 ] = d p [ i ] [ j ] [ 1 ] dp[i-1][j+1][1]=dp[i][j][1]

o t h e r w i s e otherwise ,只能让当前位为 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]

#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);
}
原创文章 430 获赞 14 访问量 1万+

猜你喜欢

转载自blog.csdn.net/C202044zxy/article/details/106119810
Fox
今日推荐