1009的奇思妙想

探讨了一个有趣的问题:如何计算特定长度下,由质数组成的大数的可能组合数量,要求任意连续四位数均为质数。文章详细介绍了使用动态规划解决这一问题的方法,包括初始化质数列表、动态转移方程的应用及最终结果的计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述

1009最近发现自己非常孤独,因为他开始有了自己的思想,并有了一个重大发现:除了1以外,他竟然不能被任何小于他的数整除!这可真是一个令人悲伤的发现呢.
后来慢慢的他意识到,他原来是一个较为特殊的数字--质数.

与此同时他还发现自己还有很多与他一样的小伙伴,如果他们能够抱团组成一个好大好大的数,这样他们就不会再孤独了.

于是他找到了和他长度相同的质数小伙伴,打算组成一个长度为x的超大数字

小伙伴们纷纷赞同1009的提议,并觉得如果单单拼在一起好像没什么难度,很无聊嘛,于是他们决定组合在一起,即他们要让组成的长度为x的数中,任意四个连续的数都是他的小伙伴(或者他自己),换句话说,就是组成的长度为x的数字中,任意连续四位都是一个质数且该质数不包含前导零.这样子大家你中有我,我中有你,只要有想组合的人(数字),就不是孤身一人(数字).

我们举个例子来说:对于98039这个数来说,连续的四位数有9803和8039,这两个数字都是质数,所以98039就算在长度为5的一种组成方法.

不过有多少种组成方式呢?1009想了很久,似乎还是想不来?那么你能解决这个问题吗?(答案对1e9+7取模)

注意:对于长度为5的位数来说,10097这种数字是不能算成一种组成方式的哦,因为连续的4位数右1009和0097,而0097虽然是一个质数,但他实际上是一个小于1000的数,即长度小于4,不是1009的小伙伴哦

        每个数字出现次数可能不止一次,且1009无需在每次组合方案中出现,比如98039也可以当成一种组合方案

输入描述

一个整数x(5 ≤ x ≤ 50000),表示他们要组成数的位数

输出描述

一个整数,表示有多少个长度为x的组成方式m,要求输出m对1e9+7的取余的结果。

样例输入

5

样例输出

1138

我们仔细想这个搜索,对于第 i 位来说,在[1,i-4]位置上的数字其实是不影响它的搜索的,能够影响到他搜索的只有 i 的前面 3 位.再往前的数字已经不会对结果产生任何影响了,这样的话我们就很容易想到使用动态规划,枚举一下后四位数,然后通过动态转移方程进行结果的累加,这里 now 表示当前枚举到的位数,j 表示枚举的后四位数 dp[now][j%1000]=dp[now][j%1000]+dp[now-1][j/10] 这样这个题的思路就差不多有了,剩下的就是一些优化和小问题了.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int dp[1010],dt[1010];
vector<int>vc;
const int mod=1e9+7;
int a[10010];

void init(){
    a[0]=a[1]=1;
    for(int i=2;i<9999;i++){
        if(!a[i]){
            for(int j=i*2;j<9999;j+=i) a[j]++;
            if(i<1000) a[i]++;
            else{
                //遇到四位质数时存在vc中
                vc.push_back(i);
                //后三位为某数时数量加一
                dt[i%1000]++;
            }
        }
    }
}

int main()
{
    init();
    int n;
    scanf("%d",&n);
    for(int i=4;i<n;i++){
        //从第4位开始到n-1位,即从第五位开始到n位,每次举出最后一位
        for(int j=0;j<vc.size();j++){
            //处理每个四位质数,前三位为k/10,后三位为k%1000,后三位可以由多少个前三位构成
            int k=vc[j];
            dp[k%1000]=(dp[k%1000]+dt[k/10])%mod;
        }
        for(int j=0;j<1000;j++){
            dt[j]=dp[j];//赋值给上一个前三位的所有结果
        }
        memset(dp,0,sizeof(dp));//dp清空计算下一个位置
    }
    int ans=0;
    for(int i=0;i<1000;i++) ans=(ans+dt[i])%mod;//把最后三位的所有可能结果都加起来
    printf("%d\n",ans);
    return 0;
}

校赛线段树:

 

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int x,y,z;
bool vis[maxn<<2];
struct node
{
    int w,d;
}t[maxn<<2];
void down(int k)
{
    t[k<<1].d=t[k].d;
    t[k<<1|1].d=t[k].d;
    t[k<<1].w=t[k].d;
    t[k<<1|1].w=t[k].d;
    t[k].d=0;
}
void add(int k,int l,int r)
{
    if(l>=x&&r<=y){
        t[k].w=z;
        t[k].d=z;
        return ;
    }
    if(t[k].d) down(k);
    int mid=(l+r)>>1;
    if(x<=mid) add(k<<1,l,mid+1);
    if(y>mid) add(k<<1|1,mid+1,r);
    t[k].w=t[k<<1].w+t[k<<1|1].w;
}
void ask(int k,int l,int r)
{
    if(r<x||l>y) return ;
    if(x<=l&&r<=y&&t[k].w!=0){
        vis[t[k].w]=1;
        return ;
    }
    if(t[k].d) down(k);
    int mid=(l+r)>>1;
    if(x<=mid){
        ask(k<<1,l,mid+1);
    }
    if(y>mid)
        ask(k<<1|1,mid+1,r);
}
int main()
{
    int n,m,k;
    bool f=0;
    scanf("%d%d%d",&n,&m,&k);
    char s[10];
    while(m--){
        scanf("%s",s);
        if(s[0]=='C'){
            scanf("%d%d%d",&x,&y,&z);
            add(1,1,n);
        }
        else if(s[0]=='Q'){
            scanf("%d%d",&x,&y);
            memset(vis,0,sizeof(vis));
            ask(1,1,n);
            int cnt=0;
            for(int i=1;i<=k;i++){
                if(vis[i]) cnt++;
            }
            printf("%d\n",cnt);
            f=1;
        }
    }
    if(f==0) printf("This is a boring game!\n");
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值