题意:给定一个含有n个数的数组a,根据这个数组可以生成一个无限整数集合(不可重),S初始为空,S中新加入的元素满足如下一条即可。
1.是a中的元素
2.是目前S中某个元素的两倍+1
3.是目前某个元素的四倍
请你求出:S的最大元素小于2^p时S中最多含有多少元素。
思路:
我们简单来看单一元素的情况。假设a中一开始只有一个元素:1
那么由1做2 3操作实际上就是二进制向后移动1个位和两个位的区别,由于第一个操作移动一个位的时候还有+1,所以每次可以获得一个新的数,也就是决策数的节点永远不出重复值
我们设dp[i]为当前这个数后移i位能得到几个数
稍微模拟一下决策树:显然有dp[1]=1 dp[2]=2 dp[i]=dp[i-1]+dp[i-2]+1
那么最大问题是,我们a中有些数是可以由别的数得来的,比如4 8就可以由1得来,所以应当把4和8去除,那么怎么去除呢,我们可以是4 8已经是决策树上的节点,这样不断网根跳,如果碰到了数组中的值直接就可以去重了。
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 2e5+100;
const int mod = 1e9+7;
int a[N];
bool vis[N],falg;
map<int,int>mp;
int dp[N];
void DFS(int now)
{
if(now<=0)
return ;
if(mp[now]==1)
{
falg=false;
return ;
}
if((now-1)%2==0)
DFS((now-1)/2);
if(now%4==0)
DFS(now>>2);
}
int cal(int n)
{
int res=-1;
while(n)
{
n>>=1;
res++;
}
return res;
}
signed main()
{
int n,p;
cin>>n>>p;
for(int i=1;i<=n;i++)
{
cin>>a[i];
mp[a[i]]=1;
}
for(int i=1;i<=n;i++)
{
falg=true;
if((a[i]-1)%2==0)
DFS((a[i]-1)/2);
if(a[i]%4==0)
DFS(a[i]>>2);
if(cal(a[i])>p)
falg=false;
vis[i]=falg;
}
dp[1]=1;
dp[2]=2;
for(int i=3;i<=200000;i++)
{
dp[i]=(dp[i-1]+dp[i-2]+1)%mod;
}
int ans=0;
for(int i=1;i<=n;i++)
{
if(vis[i])
{
ans=(ans+dp[p-cal(a[i])])%mod;
}
}
cout<<ans;
return 0;
}