报数
1.禁止报的数的生成规则与埃氏筛法类似,考虑用筛法预处理可以报出的数字列表和不可报出的数字,从而 O(1) 回答每一组询问。
具体来说,从 1 开始逐一处理每个正整数。当处理到数字 x 时,如果数字 x 尚未被标记为不合法,就通过拆位判断它是否合法。如果是合法数字,则将其加入合法数字列表,否则将 x 的所有倍数(包括 x 本身)全部标记为不合法数字;如果数字 x 已经被标记为不合法,则因为数字 x 的所有倍数都已经在之前和数字 x 一同被标为不合法数字,不需要再执行额外操作。
2.The end...
#include<bits/stdc++.h>
using namespace std;
const int N=1e7+10;
int t,la,nx[N];
bool f[N];
bool check(int x){
while(x!=0){
if(x%10==7) return 1;
x/=10;
}
return 0;
}
void pre(){
for(int i=1;i<=N;i++){
if(f[i]) continue;
if(check(i)){
for(int j=1;i*j<=N;j++){
f[i*j]=1;
}
continue;
}
nx[la]=i;
la=i;
}
return;
}
int main(){
scanf("%d",&t);
pre();
while(t--){
int x;
scanf("%d",&x);
if(f[x]) printf("-1\n");
else printf("%d\n",nx[x]);
}
return 0;
}
数列
1.可以想到dp。可以发现,S 的二进制表示位中 1 的数量会涉及进位的问题。由于进位是从低位向高位进行的,所以考虑在 S 中从低位到高位按位 dp(最低位为第 0 位)。
2.设计 dp 状态 f(i,j,k,p) 表示:讨论了 S 从低到高的前 i 位,已经确定了 j 个序列 a 中的元素,S 从低到高前 i 位中有 k 个 1,要向当前讨论位的下一位进位 p。因为从上一个状态转移到 f(i,j,k,p) 细节太多,所以考虑用 f(i,j,k,p) 往后转移。接下来讨论第 i 位(位从 0 开始编号)。假设序列 a 中有 t 个元素为 i,那么就相当于给 S 的第 i 位贡献了 t 个 1,再加上上一位进过来的 p 个 1,总共有 t+p 个 1。可以知道,当前位每两个 1 可以向下一位进 1。所以 (t+p)%2 的结果即为全部进位后当前位是否为 1。同理,向下一位进的 1 的个数即为 ⌊t+p2⌋/2。
f(i,j,k,p) 往后转移的状态为f(i+1,j+t,k+(t+p)%2,⌊2t+p⌋