2020牛客NOIP赛前集训营-提高组(第二场)
Before the solution
这场数据实在是 太 弱 了
我和 @ZigZagKmp 分别造了 B 和 D 的 hack 数据,并一通操作之后联系上了命题人,命题人将 hack 数据加入了正式数据。
希望能够重测吧 2020.10.22 7:44
upd2020/10/22 14:30 重测完了,我排名没变?
A GCD
Statement
我们定义 \(f(x)=\gcd(x\) 除 \(1\) 之外的所有因子 \()\)。
即 \(x\) 除 \(1\) 外所有因子的 \(gcd\)。
询问 \(\sum_{i=a}^b f(i)\)。
题解
考虑这些数,除了可以表示为 \(x=p^k\) 形式的数,其他的数 \(x\) 都至少有两个质因子,即 \(f(x)=1\)
也就是说 \(f(x) = \begin{cases} p & x = p^k\\ 1 & \text{otherwise} \end{cases} \)
用欧拉筛晒出 \([1,b]\) 中的质数,再枚举这些质数的次幂即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
template < typename Tp >
inline void read(Tp &x) {
x = 0; int fh = 1; char ch = 1;
while(ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if(ch == '-') fh = -1, ch = getchar();
while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
x *= fh;
}
template < typename Tp >
inline void biread(Tp &x) {
x = 0; int fh = 1; char ch = 1;
while(ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if(ch == '-') fh = -1, ch = getchar();
while(ch >= '0' && ch <= '9') x = x * 2 + ch - '0', ch = getchar();
x *= fh;
}
bool p[10000007];
signed prim[10000007], cnt;
int a, b;
void prime(void) {
p[1] = true;
for(int i = 2; i <= b; i++) {
if(p[i] == false) prim[++cnt] = i;
for(int j = 1; j <= cnt && i * prim[j] <= b; j++) {
p[i * prim[j]] = true;
if(i % prim[j] == 0) break;
}
}
}
inline void Init(void) {
read(a); read(b);
}
long long ans;
inline void Work(void) {
prime();
for(signed i = 1; i <= cnt; i++) {
// int j = prim[i];
for(long long j = prim[i]; j <= (long long)b; j = j * (long long)prim[i]) {
if(j >= a) ans += prim[i] - 1;
}
}
ans += (long long)b - (long long)a + 1ll;
// for(int i = a; i <= b; i++) {
// if(p[i]) ans++;
// else ans += i;
// }
printf("%lld\n", ans);
}
signed main(void) {
Init();
Work();
return 0;
}
B 包含
题外话
进行 Hack 活动时校 OJ 的场景。
Statement
我们定义 \(A\) “包含” \(B\) 的概念是 \(A \operatorname{and} B=B\) ,其中 \(\operatorname{and}\) 是位运算中的“按位与”。
现在给出一个集合 \(Q\) ,这个集合 \(n\) 个正整数,\(m\) 次询问。每次询问给出一个数字 \(x\) ,请回答集合 \(Q\) 中是否有一个数字包含 \(x\) 。
题解
观察到值域为 \(10^6\) ,肯定从值域搞事情,很容易想到枚举是否删除二进制位。
发现 \(n\) 远小于 \(T\),直接预处理值域内的答案, \(O(1)\) 回答每个询问即可。
可以通过 dfs 每一个输入的 \(a_i\),对于 \(a_i\) 中每个为 \(1\) 的二进制位,dfs 是否删除。
同时,通过记忆化优化时间,因为如果一个数 \(p\) 已经被搜索过,就没必要再对 \(p\) 进行处理。
时间复杂度为 \(O(\text{值域}+T)\)
代码
#include<bits/stdc++.h>
using namespace std;
template < typename Tp >
inline void read(Tp &x) {
x = 0; int fh = 1; char ch = 1;
while(ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if(ch == '-') fh = -1, ch = getchar();
while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
x *= fh;
}
template < typename Tp >
inline void biread(Tp &x) {
x = 0; int fh = 1; char ch = 1;
while(ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if(ch == '-') fh = -1, ch = getchar();
while(ch >= '0' && ch <= '9') x = x * 2 + ch - '0', ch = getchar();
x *= fh;
}
const int maxn = 100000 + 7;
int n, T;
bool quality[1000007];
////vector <int> v;
void dfs(int x, int step, int p2) {
if(p2 > x) return ;
if(quality[x]) return ;
if(((x >> step) & 1) == 0) {
dfs(x, step + 1, p2 * 2);
return ;
}
dfs(x - p2, step + 1, p2 * 2);
quality[x - p2] = true;
dfs(x, step + 1, p2 * 2);
quality[x] = true;
}
inline void Init(void) {
read(n); read(T);
for(int i = 1, x; i <= n; i++) {
read(x);
dfs(x, 0, 1); quality[x] = true;
}
}
inline void Work(void) {
while(T--) {
int x; read(x);
if(quality[x]) puts("yes");
else puts("no");
}
}
signed main(void) {
Init();
Work();
return 0;
}
C 前缀
Statement
牛牛有一个 \(s\) 串, \(s\) 串仅由 \(26\) 个小写英文字母组成,他现在将 \(s\) 串进行了无限次的复制扩展成了一个无限循环串。
例如一开始 s="abc",那么牛牛就会将其变为 "abcabcabc..."
若某个字符串保留其原本字符出现的顺序,并且按照顺序取出若干个字符。可以不连续,可以不取。
我们称取出的这若干个字符连成的字符串为一个子序列。
若连续取出某个字符串的前 \(k\) 个字符,组成一个子串,我们称该字符串为原串长度为 \(k\) 的前缀。
对于一个字符串 \(t\) ,若某字符串的至少一个子序列为 \(t\) 。则称它是一个“含 \(t\) 序列串”
牛牛想要知道对于给定的 \(t\) ,他想要知道 \(s\) 的一个最短前缀满足它是一个“含 \(t\) 序列串”,它的长度有多长?
由于答案可能非常大,所以他要求你输出答案对 \(998244353\) 取余数后的结果即可。
特别的,如果S串不存在任何一个前缀满足他是一个“含 \(t\) 序列串”,请输出" \(-1\) "表示无解。
\(t\) 串中除了 \(26\) 个英文字母以外还会出现"*",表示一个通配符。统配符可以视为任意字母。
例如循环\(s\)串为"\(abcabcabcabc\)...",t串为"\(a*ca\)"时,最短含\(t\)序列前缀长\(4\)。而当\(t\)串为"\(a**ca\)"时,最短含\(t\)序列前缀长\(7\)。
除此之外,牛牛输入的t串还可能非常非常长,最长可以达到\(10^{10^{5}}\)这么长。
所以他想了一种压缩方法,来快速读入\(t\)串。
具体来说,它输入的\(t\)串中除了"*"和\(26\)个小写英文字母以外,还会跟有一些正整数。
在读入字符串时,这些数字表示它前面字母或者"*"重复的次数。
例如\(a5bc*3\),表示"\(aaaaabc***\)"。输入的正整数不含前导\(0\)。
题解
恶臭大模拟。
代码
D 移动
Statement
牛牛被困在了一个房间里,他可以看到房间的出口,但是想要到达出口,需要经过 \(n\) 道闸门。我们可以根据这些闸门离牛牛的距离进行编号,离牛牛最近的闸门记为 \(1\) 号闸门,离牛牛最远的记为 \(n\) 号闸门。
牛牛每秒都可以选择前进到下一闸门,后退到上一闸门,或者原地不动。(从起点到第一道闸门,从第 \(n\) 道闸门到出口的时间也是一秒)
这些闸门在一些时刻是关闭的,无法通行,剩下的时刻是开启的,可以通行。
注意:如果牛牛所在的位置有一个闸门即将关闭,他在此时选择原地不动,就会被闸门夹到,变成牛排。牛牛想在不变成牛排的前提下走到出口,他想知道最短需要多少秒才能走到出口,如果他永远无法走到出口,输出 \(-1\) 。
在每一秒内,首先牛牛进行移动,然后闸门进行开/关的动作。
题解
代码