[赛记] 多校A层冲刺NOIP2024模拟赛04

这场ACCODERS忘交,结果最后想起来匆匆只交了T1,然后文件名还没改,所以爆零了。。。

02表示法 100pts

高精度,不说了;

点击查看代码
#include <iostream>
#include <cstdio>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;
string p[605];
string ans;
string p2[605];
string n;
string jia(string x, string y) {
	string ans;
	ans.clear();
	for (int i = 1; i <= 205; i++) {
		ans = ans + '0';
	}
	bool jin = false;
	for (int i = 1; i <= 200; i++) {
		int res = (x[i] - '0') + (y[i] - '0') + jin;
		if (res >= 10) {
			jin = true;
			res %= 10;
		} else {
			jin = false;
		}
		ans[i] = res + '0';
	}
	return ans;
}
int cmp(string x, string y) {
	reverse(x.begin(), x.end());
	reverse(y.begin(), y.end());
	for (int i = 1; i < x.size(); i++) {
		if ((x[i] - '0') > (y[i] - '0')) return 1;
		if ((x[i] - '0') < (y[i] - '0')) return 2;
	}
	return 0;
}
string jian(string x, string y) {
	string ans;
	ans.clear();
	for (int i = 1; i <= 205; i++) {
		ans = ans + '0';
	}
	for (int i = 1; i <= 200; i++) {
		int xres = (x[i] - '0');
		int yres = (y[i] - '0');
		if (xres < yres) {
			int pos = i;
			for (int j = i + 1; j <= 200; j++) {
				if ((x[j] - '0') > 0) {
					x[j] = x[j] - 1;
					pos = j;
					break;
				}
			}
			xres += 10;
			for (int j = i + 1; j < pos; j++) x[j] = '9';
		}
		int now = xres - yres;
		ans[i] = now + '0';
	}
	return ans;
}
int main() {
	freopen("pow.in", "r", stdin);
	freopen("pow.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	reverse(n.begin(), n.end());
	n = '0' + n;
	int now = n.size();
	for (int j = now + 1; j <= 205; j++) {
		n = n + '0';
	}
	for (int i = 0; i <= 600; i++) {
		for (int j = 1; j <= 205; j++) {
			p2[i] = p2[i] + '0';
		}
	}
	p2[0][1] = '1';
	for (int i = 1; i <= 600; i++) p2[i] = jia(p2[i - 1], p2[i - 1]);
	p[0] = "0";
	p[1] = "2(0)";
	p[2] = "2";
	p[3] = "2+2(0)";
	p[4] = "2(2)";
	for (int i = 5; i <= 600; i++) {
		int x = i;
		for (int j = 10; j >= 0; j--) {
			if (x >= pow(2, j)) {
				x -= pow(2, j);
				if (p[i].empty()) {
					if (j == 1) p[i] = "2";
					else p[i] = "2(" + p[j] + ")";
				} else {
					if (j == 1) p[i] = p[i] + "+2";
					else p[i] = p[i] + "+2(" + p[j] + ")";
				}
			}
		}
	}
	for (int j = 600; j >= 0; j--) {
		if (cmp(n, p2[j]) != 2) {
			n = jian(n, p2[j]);
			if (ans.empty()) {
				if (j == 1) ans = "2";
				else ans = "2(" + p[j] + ")";
			} else {
				if (j == 1) ans = ans + "+2";
				else ans = ans + "+2(" + p[j] + ")";
			}
		}
	}
	cout << ans;
	return 0;
}

子串的子串 70pts

暴力分挺足,70pts;

正解就是预处理 $ ans_{l, r} $ 代表答案,枚举每个子区间,然后如果有重复的就将这整个区间 $ -1 $;

考虑这样处理有什么好处,我们最后用二维前缀和倒序处理每个 $ ans $,这样我们就巧妙地去掉了所有重复的子区间且不会多减(这也是为什么不正序的原因);

时间复杂度:$ \Theta(n^2 + q) $,注意要使用 $ Hash $ 判等;

点击查看代码
#include <iostream>
#include <cstdio>
#include <string>
#include <map>
using namespace std;
const unsigned long long pep = 229;
int n, q;
char s[5005];
unsigned long long h[5005], p[5005];
map<unsigned long long, int> mp;
int ans[5005][5005];
int main() {
	freopen("substring.in", "r", stdin);
	freopen("substring.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> q;
	for (int i = 1; i <= n; i++) {
		cin >> s[i];
	}
	p[0] = 1;
	for (int i = 1; i <= n; i++) {
		p[i] = p[i - 1] * pep;
		h[i] = h[i - 1] * pep + s[i];
	}
	for (int len = 1; len <= n; len++) {
		mp.clear();
		for (int l = 1; l + len - 1 <= n; l++) {
			int r = l + len - 1;
			unsigned long long now = h[r] - h[l - 1] * p[r - l + 1];
			ans[mp[now]][r]--;
			mp[now] = l;
		}
	}
	for (int l = n; l >= 1; l--) {
		for (int r = l; r <= n; r++) {
			ans[l][r] += ans[l + 1][r] + ans[l][r - 1] - ans[l + 1][r - 1] + 1;
		}
	}
	int l, r;
	for (int i = 1; i <= q; i++) {
		cin >> l >> r;
		cout << ans[l][r] << '\n';
	}
	return 0;
}

其实这题难点在于没有去想 $ \Theta(n^2 + q) $ 的做法,而是去想了 $ \Theta(nq) $ 的做法;

魔法咒语 -pts

赛时打T4,没写T3;

对于正解,我们正反向各建一棵 Trie,那么最原始的答案就是两个 Trie 的节点相乘;

但是有重的,考虑重复的原因在于一个串可能被划分两次;

比如abc 可以被划分成 abc,也可以被划分成 abc,对于这种情况,我们钦定划分点来自于前缀,这样就避免了;

具体实现上,我们遍历前缀树,对于每个节点,我们不统计其儿子在后缀树上的贡献,这样就去重了;

但是如果这个字符就是最后一个的话,按照原来的算法无法找到这个字符;

比如有 abcc,找不到 abc

但如果直接加 $ n $ 则可能会与之前拼接好的重复;

所以我们记一下每个串的结尾,然后如果是结尾加上即可;

但是如果原串长度为 $ 1 $,则无法被统计上,所以特判一下即可;

时间复杂度:$ \Theta(n \log n) $;

点击查看代码
#include <iostream>
#include <cstdio>
#include <string>
#include <map>
using namespace std;
int n;
string a[50005];
long long ans;
int tot1, tot2, cnt[28];
namespace Trie{
	int son1[500005][28], son2[500005][28];
	void add(string s, bool is) {
		if (!is) {
			int now = 0;
			for (int i = 0; i < s.size(); i++) {
				if (!son1[now][s[i] - 'a']) son1[now][s[i] - 'a'] = ++tot1;
				now = son1[now][s[i] - 'a'];
			}
		} else {
			int now = 0;
			for (int i = s.size() - 1; i >= 0; i--) {
				if (!son2[now][s[i] - 'a']) {
					son2[now][s[i] - 'a'] = ++tot2;
					cnt[s[i] - 'a']++;
				}
				now = son2[now][s[i] - 'a'];
			}
		}
	}
}
using namespace Trie;
bool vis[28], vi[28];
int main() {
	freopen("magic.in", "r", stdin);
	freopen("magic.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	for (int i = 1; i <= n; i++) {
		add(a[i], 1);
		add(a[i], 0);
		vis[a[i][a[i].size() - 1] - 'a'] = true;
		if (a[i].size() == 1 && !vi[a[i][0] - 'a']) {
			vi[a[i][0] - 'a'] = true;
			ans++;
		}
	}
	for (int i = 1; i <= tot1; i++) {
		for (int j = 0; j <= 25; j++) {
			if (!son1[i][j]) {
				ans += cnt[j];
			} else if (vis[j]) {
				ans++;
			}
		}
	}
	cout << ans;
	return 0;
}

表达式 35pts

乱打35pts;

暴力:我们可以用线段树来维护每个 $ x $ 对应的这些操作的结果,可以通过小模数的数据;

正解:发现题目中除暴力给出的模数都不是质数,且进行唯一分解定理得到的互质的数数量都不多,大小也不大;

所以进行拆分,然后得到一堆互质的数,我们分别算出对于每个数的每个 $ x $ 对应的结果,然后会得到一个同余方程组,用 $ CRT $ 求解即可;

也是复习了一下 $ CRT $;

时间复杂度:设 $ |p| $ 为分解出的互质的数的大小之和,则时间复杂度为 $ \Theta(|p| n \log n) $;

比较卡空间和时间;

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int t;
long long n, m, mod;
struct sss{
	char s;
	long long a;
}e[500005];
long long ksm(long long a, long long b, long long p) {
	long long ans = 1;
	while(b) {
		if (b & 1) ans = ans * a % p;
		a = a * a % p;
		b >>= 1;
	}
	return ans;
}
long long exgcd(long long a, long long b, long long &x, long long &y) {
	if (b == 0) {
		x = 1;
		y = 0;
		return a;
	}
	long long ret = exgcd(b, a % b, x, y);
	long long o = x;
	x = y;
	y = o - a / b * y;
	return ret;
}
long long w(long long x) {
	for (int i = 1; i <= n; i++) {
		if (e[i].s == '+') x = (x + e[i].a) % mod;
		if (e[i].s == '*') x = (x * e[i].a) % mod;
		if (e[i].s == '^') x = ksm(x, e[i].a, mod);
	}
	return x;
}
long long pri[500005], cnt, inv[500005];
namespace SEG{
	inline int ls(int x) {
		return x << 1;
	}
	inline int rs(int x) {
		return x << 1 | 1;
	}
	struct sss{
		long long l, r, val[8][31];
	}tr[525005];
	inline void push_up(int id) {
		for (int i = 1; i <= cnt; i++) {
			for (int j = 0; j < pri[i]; j++) {
				tr[id].val[i][j] = tr[rs(id)].val[i][tr[ls(id)].val[i][j]];
			}
		}
	}
	inline void upd(int id, int x) {
		for (int i = 1; i <= cnt; i++) {
			for (int j = 0; j < pri[i]; j++) {
				if (e[x].s == '+') {
					tr[id].val[i][j] = (j + e[x].a) % pri[i];
				}
				if (e[x].s == '*') {
					tr[id].val[i][j] = (j * e[x].a) % pri[i];
				}
				if (e[x].s == '^') {
					tr[id].val[i][j] = ksm(j, e[x].a, pri[i]);
				}
			}
		}
	}
	void bt(int id, int l, int r) {
		tr[id].l = l;
		tr[id].r = r;
		if (l == r) {
			upd(id, l);
			return;
		}
		int mid = (l + r) >> 1;
		bt(ls(id), l, mid);
		bt(rs(id), mid + 1, r);
		push_up(id);
	}
	void add(int id, int pos) {
		if (tr[id].l == tr[id].r) {
			upd(id, tr[id].l);
			return;
		}
		int mid = (tr[id].l + tr[id].r) >> 1;
		if (pos <= mid) add(ls(id), pos);
		else add(rs(id), pos);
		push_up(id);
	}
}
int main() {
	freopen("expr.in", "r", stdin);
	freopen("expr.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> t;
	cin >> n >> m >> mod;
	for (int i = 1; i <= n; i++) {
		cin >> e[i].s;
		cin >> e[i].a;
	}
	if (mod == 1) {
		int s;
		long long x;
		int p;
		char cc;
		for (int i = 1; i <= m; i++) {
			cin >> s;
			if (s == 1) {
				cin >> x;
				cout << 0 << '\n';
			}
			if (s == 2) {
				cin >> p;
				cin >> cc;
				cin >> x;
			}
		}
		return 0;
	}
	if (t <= 3) {
		int s;
		long long x;
		int p;
		char cc;
		for (int i = 1; i <= m; i++) {
			cin >> s;
			if (s == 1) {
				cin >> x;
				cout << w(x) << '\n';
			}
			if (s == 2) {
				cin >> p;
				cin >> cc;
				cin >> x;
				e[p].s = cc;
				e[p].a = x;
			}
		}
		return 0;
	}
	long long p = mod;
	long long tx = 0, ty = 0;
	for (int i = 2; i * i <= mod; i++) {
		if (p % i == 0) {
			long long y = 1;
			while(p % i == 0) {
				y *= i;
				p /= i;
			}
			pri[++cnt] = y;
			exgcd(mod / y, y, tx, ty);
			inv[cnt] = (tx % y + y) % y;
		}
	}
	if (p != 1) {
		pri[++cnt] = p;
		exgcd(mod / p, p, tx, ty);
		inv[cnt] = (tx % p + p) % p;
	}
	SEG::bt(1, 1, n);
	int s;
	long long x;
	int pp;
	for (int i = 1; i <= m; i++) {
		cin >> s;
		if (s == 1) {
			cin >> x;
			long long ans = 0;
			for (int i = 1; i <= cnt; i++) {
				ans = (ans + mod / pri[i] * inv[i] % mod * SEG::tr[1].val[i][x % pri[i]] % mod) % mod;
			}
			cout << ans << '\n';
		}
		if (s == 2) {
			cin >> pp;
			cin >> e[pp].s;
			cin >> e[pp].a;
			SEG::add(1, pp);
		}
	}
	return 0;
}
posted @   Peppa_Even_Pig  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?
点击右上角即可分享
微信分享提示