问题描述
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;
}