D. Infinite Set 题解(思维+dp)

题目链接

题目思路

首先要观察它需要的数是小于\(2^p\),那么需要往二进制的思路去想就简单很多

\(x=2y+1\) 本质上就是在二进制的表现下末尾加一个1

\(x=4y\) 本质上就是在二进制的表现下末尾加两个\(00\)

那么如果一个二进制长度为\(len\)的数\(x\)

它要延申成长度为\(len+1\)位有\(1\)

它要延申成长度为\(len+2\)位有\(2\)

它要延申成长度为\(len+3\)位有\(3\)

这就是斐波那契数列

定义\(\sum_{i=0}^{i=n} fac[i]=sum[n]\)

那么如果他要延申成长度小于\(p\)位有\(sum[p-len]\)

那么对于一个数的情况解决了,那么对于多个数的情况就是需要去重

加入\(x\)可以生成\(y\),那么需要去掉\(y\)

那么假如\(y\)是奇数,\(y/=2\) 。假如\(y\)\(4\)的倍数,\(y/=4\) 看中途是否出现过即可

代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
const int maxn=2e5+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-6;
int n,p;
int a[maxn];
ll ans;
ll fac[maxn],sum[maxn];
map<int, bool> mp;
int cal(int x){
    int len=0;
    while(x){
        x/=2;
        len++;
    }
    return len;
}
signed main(){
    fac[0]=fac[1]=1;
    sum[0]=1;
    sum[1]=2;
    for(int i=2;i<=2e5;i++){
        fac[i]=(fac[i-1]+fac[i-2])%mod;
        sum[i]=(sum[i-1]+fac[i])%mod;
    }
    scanf("%d%d",&n,&p);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        mp[a[i]]=true;
    }
    for(int i=1;i<=n;i++){
        int tmp=a[i];
        bool flag=false;
        while(tmp){
            if(tmp%2){
                tmp/=2;
            }else if(tmp%4==0){
                tmp/=4;
            }else{
                break;
            }
            if(mp.count(tmp)){
                flag=true;
                break;
            }
        }
        if(flag) continue;
        int len=cal(a[i]);
        if(p-len>=0){
            ans=(ans+sum[p-len])%mod;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

题目链接

题目思路

首先要观察它需要的数是小于\(2^p\),那么需要往二进制的思路去想就简单很多

\(x=2y+1\) 本质上就是在二进制的表现下末尾加一个1

\(x=4y\) 本质上就是在二进制的表现下末尾加两个\(00\)

那么如果一个二进制长度为\(len\)的数\(x\)

它要延申成长度为\(len+1\)位有\(1\)

它要延申成长度为\(len+2\)位有\(2\)

它要延申成长度为\(len+3\)位有\(3\)

这就是斐波那契数列

定义\(\sum_{i=0}^{i=n} fac[i]=sum[n]\)

那么如果他要延申成长度小于\(p\)位有\(sum[p-len]\)

那么对于一个数的情况解决了,那么对于多个数的情况就是需要去重

加入\(x\)可以生成\(y\),那么需要去掉\(y\)

那么假如\(y\)是奇数,\(y/=2\) 。假如\(y\)\(4\)的倍数,\(y/=4\) 看中途是否出现过即可

代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
const int maxn=2e5+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-6;
int n,p;
int a[maxn];
ll ans;
ll fac[maxn],sum[maxn];
map<int, bool> mp;
int cal(int x){
    int len=0;
    while(x){
        x/=2;
        len++;
    }
    return len;
}
signed main(){
    fac[0]=fac[1]=1;
    sum[0]=1;
    sum[1]=2;
    for(int i=2;i<=2e5;i++){
        fac[i]=(fac[i-1]+fac[i-2])%mod;
        sum[i]=(sum[i-1]+fac[i])%mod;
    }
    scanf("%d%d",&n,&p);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        mp[a[i]]=true;
    }
    for(int i=1;i<=n;i++){
        int tmp=a[i];
        bool flag=false;
        while(tmp){
            if(tmp%2){
                tmp/=2;
            }else if(tmp%4==0){
                tmp/=4;
            }else{
                break;
            }
            if(mp.count(tmp)){
                flag=true;
                break;
            }
        }
        if(flag) continue;
        int len=cal(a[i]);
        if(p-len>=0){
            ans=(ans+sum[p-len])%mod;
        }
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2022-02-21 12:01  hunxuewangzi  阅读(196)  评论(0编辑  收藏  举报