1.报数
像今年 T1 这种几年不遇的水题你是不可能再遇见第二次的。
这道题提前预处理单纯地筛一筛就可以了,没有别的任何操作。
需要注意的是,在预处理时要合理的剪枝,保证时间复杂度控制在 (
是数据范围T是询问数)。
我们用f数组表示该数是否被标记。如果一个数i被标记过了,就直接跳过;如果i含有数字7,我们就将i的所有倍数(包括i本身)全部标记。
我们用 nx数组(也就是 next的缩写)来记录该数的下一个报的数是多少。在处理的时候,我们需要记录上一个报的数 ls(last 的缩写,也就是没有标记的数)。如果 i没有标记过也不含有数字 7,那么就是 i,然后将 ls 更新为 i。
预处理的好处就是保证询问的时候每次询问都是 。如果询问的 xx 被标记了,就输出 -1;反之,输出
。
还要注意一点,考场上一定要用上读入优化和输出优化,小心可能会被卡。
代码非常的好写,看看就可以了:
#include <bits/stdc++.h>
using namespace std;
inline int read()//读入优化
{
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
f = ch != '-';
ch = getchar();
}
while (isdigit(ch))
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return f ? x : -x;
}
inline void write(int x)//输出优化
{
if (x >= 10)
write(x / 10);
putchar(x % 10 + 48);
}
const int N = 1e7 + 100;
int T, x, ls;
int f[N], nx[N];
bool check(int x)//判断是否含有数字7
{
while (x)
{
if (x % 10 == 7)
return 1;
x /= 10;
}
return 0;
}
void init()//预处理部分
{
for (int i = 1; i <= N - 10; i++)
{
if (f[i])//如果被标记过,就跳过
continue;
if (check(i))//如果含有数字7,标记其倍数
{
for (int j = i; j <= N - 10; j += i)
f[j] = 1;
continue;
}
nx[ls] = i;//记录i
ls = i;//更新ls
}
}
int main()
{
init();//先预处理
T = read();
while (T--)
{
x = read();
if (f[x])//被标记了输出-1,否则输出nx
puts("-1");
else
write(nx[x]), putchar('\n');
}
return 0;
}
2.数列
我就按照我考场上的思考过程讲吧。
考虑 DP。(计数类不是组合数就是 DP 呀)
我们定义一个四维的状态表示已经填好了 i个数,考虑完了
,此时 S二进制下有 k个 1,有 q个已经赋值了的
取了 j,且 q≥1 时的权值总和。
我们发现这个状态不是很好转移,因为我们无法通过 q转移 k。问题就在于,如何计算 k 呢?
比如说我们现在取了 x 个 ,那么若
则等价于在二进制下 S 的 y+i位上有一个 11 的贡献,所以对 k 的贡献为
。
表示 x 在二进制下 1 的位数。也就是说,我们如果单考虑 S 二进制下 1 的个数,取 x 个
等价于取
个
。 也就是说,后几位的数对于前面可能会有影响,我们可以把状态改成这样:
四维的状态表示已经填好了 i 个数,考虑完了
,此时 S 二进制下有 k 个 1,若不考虑 j以后的数,等价于取了 q 个 j,且至少有一个是本身就取了 j。
这个就很好转移了。我们有转移方程:
如何理解这个方程呢? x 表示有多少个数本身就取了 j,p 是上一个本身被取的数字,由于我们要倒退上一个状态我们的 k 就会改变。我们少了 x 个数,不会对更低位上的 1 产生影响,因此 。而 tmp 就是 p 这一位等价的个数。
我们容易发现,这个代码复杂度大约为 。但是跑不满。其实严重跑不过,只有 50pts。
我们考虑优化,我们重新定义一下状态,如下:
四维的状态