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\)

在每一秒内,首先牛牛进行移动,然后闸门进行开/关的动作。

题解

代码


posted @ 2020-10-20 21:55  览遍千秋  阅读(314)  评论(0编辑  收藏  举报