[赛记] NOIP2024加赛2

新的阶乘 100pts

当发现直接做不好做时,可以从贡献的角度考虑;

那么对于一个质数,我们只需要算出它的所有贡献,直接暴力算即可;

复杂度和埃氏筛差不多,Θ(nloglogn)

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int n;
long long ans[10000005], an[10000005];
long long pri[10000005];
bool vis[10000005];
int cnt;
void p() {
	for (int i = 2; i <= n; i++) {
		if (!vis[i]) {
			pri[++cnt] = i;
			for (int j = 2; j * i <= n; j++) {
				vis[j * i] = true;
			}
		}
	}
}
int main() {
	freopen("factorial.in", "r", stdin);
	freopen("factorial.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	p();
	for (int i = 1; i <= cnt; i++) {
		an[pri[i]] = 1;
		for (int j = 1; j * pri[i] <= n; j++) {
			ans[pri[i]] += (an[j] + 1) * (n - (j * pri[i] - 1));
			an[j * pri[i]] = an[j] + 1;
		}
		an[pri[i]] = 0;
		for (int j = 1; j * pri[i] <= n; j++) {
			an[j * pri[i]] = 0;
		}
	}
	cout << "f(" << n << ")=";
	int pos = 0;
	for (int i = 1; i <= n; i++) {
		if (ans[i]) pos = i;
	}
	for (int i = 1; i <= n; i++) {
		if (ans[i]) {
			if (i != pos) {
				if (ans[i] == 1) cout << i << "*";
				else cout << i << "^" << ans[i] << "*";
			} else {
				if (ans[i] == 1) cout << i;
				else cout << i << "^" << ans[i];
			}
		}
	}
	return 0;
}

博弈树 0pts

考虑这玩意会和直径有关,Bob能赢当且仅当直径所包含的点数为奇数且这个点为直径中点,否则不管Bob怎么走,Alice总能复制Bob的行走路线走到以直径中点为对称中心的对称点,最后将Bob逼到直径的一个端点处而取得胜利;

于是找直径即可,时间复杂度:Θ(n)

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int n, q;
struct sss{
	int t, ne;
}e[500005];
int h[500005], cnt;
void add(int u, int v) {
	e[++cnt].t = v;
	e[cnt].ne = h[u];
	h[u] = cnt;
}
int dis[500005], fa[500005];
void dfs(int x, int f) {
	dis[x] = dis[f] + 1;
	fa[x] = f;
	for (int i = h[x]; i; i = e[i].ne) {
		int u = e[i].t;
		if (u == f) continue;
		dfs(u, x);
	}
}
int main() {
	freopen("tree.in", "r", stdin);
	freopen("tree.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> q;
	int x, y;
	for (int i = 1; i <= n - 1; i++) {
		cin >> x >> y;
		add(x, y);
		add(y, x);
	}
	dfs(1, 0);
	int ma = 0, pos = 0;
	for (int i = 1; i <= n; i++) {
		if (dis[i] > ma) {
			pos = i;
			ma = dis[i];
		}
	}
	for (int i = 1; i <= n; i++) dis[i] = 0;
	dfs(pos, 0);
	ma = 0;
	int po = 0;
	for (int i = 1; i <= n; i++) {
		if (dis[i] > ma) {
			po = i;
			ma = dis[i];
		}
	}
	ma--;
	int ans = 0;
	if (!(ma & 1)) {
		while(po != pos) {
			if (dis[po] - 1 == ma / 2) {
				ans = po;
				break;
			}
			po = fa[po];
		}
	}
	for (int i = 1; i <= q; i++) {
		cin >> x;
		if (n == 1) {
			cout << "Bob" << '\n';
			continue;
		}
		if (x == ans) cout << "Bob" << '\n';
		else cout << "Alice" << '\n';
	}
	return 0;
}

划分 9pts

这个题很容易想到DP,且很容易写,然后就得了9pts;

问题在于取模以后无法比较大小;

所以正解不是DP;

我们不难发现,最后分的段越少越好,但是我们要分以下几种情况讨论;

  1. k 位全是 0

将后面从 1 开始的数全分成一段是最优的,方案数直接对前面的一堆 0 插板即可;

  1. n=k

方案数为 1,答案为 1 的个数;

  1. 全是 0

方案数直接插板,答案为 0

  1. 普通情况;

发现将原序列分成一个长度为 nk+1 的段,剩下的每个数单独成段是不劣的,所以考虑这么分;

我们发现,对于那个长度为 nk+1 的段,我们想让它最大,需要让前 nk 位所组成的数最大,最后一位没有影响(因为 1 单独成一段和并到这一段的贡献都是 1,所以最后一位可以是 10 );

那么我们用最大表示法找出来最大的 nk 位所组成的数,这样的数的个数就是方案数(注意第 nk 位在原字符串中不能是第 n 位),用Hash找一下即可;

然后简单统计一下答案即可(不要忘了最大的 nk 位所组成的数要乘 2 );

时间复杂度:Θ(nlogn) (也可以通过预处理 2 的次方和线性求逆元去掉 log );

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
const long long mod = 998244353;
const long long pep = 229;
int n, k;
char s[5000005];
long long fac[5000005], fav[5000005];
long long h[5000005], p[5000005];
long long ksm(long long a, long long b) {
	long long ans = 1;
	while(b) {
		if (b & 1) ans = ans * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return ans;
}
long long C(long long a, long long b) {
	if (a < b) return 0;
	return fac[a] * fav[b] % mod * fav[a - b] % mod;
}
int main() {
	freopen("divide.in", "r", stdin);
	freopen("divide.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> k;
	cin >> (s + 1);
	fac[0] = 1;
	fav[0] = 1;
	for (int i = 1; i <= n; i++) {
		fac[i] = fac[i - 1] * i % mod;
		fav[i] = ksm(fac[i], mod - 2);
	}
	if (n == k) {
		long long ans = 0;
		for (int i = 1; i <= n; i++) {
			ans += (s[i] == '1');
		}
		cout << ans << ' ' << 1;
		return 0;
	}
	int pos = 0;
	for (int i = 1; i <= n; i++) {
		if (s[i] == '1') {
			pos = i;
			break;
		}
	}
	if (pos > k) {
		long long ans = 0;
		for (int i = k - 1; i <= pos - 1; i++) {
			ans = (ans + C(pos - 1, i)) % mod;
		}
		long long sum = 0;
		for (int i = n; i >= pos; i--) {
			if (s[i] == '1') {
				sum = (sum + ksm(2, n - i)) % mod;
			}
		}
		cout << sum << ' ' << ans;
		return 0;
	}
	int i = 1, j = 2;
	while(i <= k && j <= k) {
		int x = 0;
		for (x = 0; x <= n - k - 1; x++) {
			if (s[i + x] != s[j + x]) break;
		}
		if (s[i + x] < s[j + x]) {
			i += (x + 1);
		} else {
			j += (x + 1);
		}
		if (i == j) j++;
	}
	pos = min(i, j);
	h[1] = s[1];
	p[0] = 1;
	p[1] = pep;
	for (int i = 2; i <= n; i++) {
		h[i] = (h[i - 1] * pep % mod + s[i]) % mod;
		p[i] = p[i - 1] * pep % mod;
	}
	long long sum = ((h[pos + n - k - 1] - h[pos - 1] * p[n - k] % mod) + mod) % mod;
	long long ans = 0;
	for (int i = 1; i + n - k - 1 <= n - 1; i++) {
		if ((h[i + n - k - 1] - h[i - 1] * p[n - k] % mod + mod) % mod == sum) ans++;
	}
	sum = 0;
	long long ss = 0;
	for (int i = pos + n - k - 1; i >= pos; i--) {
		if (s[i] == '1') {
			sum = (sum + ksm(2, (pos + n - k - 1) - i + 1)) % mod;
			ss++;
		}
	}
	long long su = 0;
	for (int i = 1; i <= n; i++) su += (s[i] == '1');
	if (su == 0) {
		sum = 0;
		for (int i = k - 1; i <= n - 1; i++) {
			sum = (sum + C(n - 1, i)) % mod;
		}
		cout << 0 << ' ' << sum;
		return 0;
	}
	cout << sum + su - ss << ' ' << ans;
	return 0;
}
posted @   Peppa_Even_Pig  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示