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

字符串构造机 100pts

原题,见[赛记] 多校A层冲刺NOIP2024模拟赛01【衡中】 T1;

忍者小队 60pts

赛时最后想出来个 $ \Theta(n^2 \log n) $ 的 DP,所以60pts;

对于这个DP,直接用 map 维护一下所有lcm的状态转移即可;

点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;
int n, m;
int a[500005];
int vis[500005];
int ma;
vector<int> v[500005];
int gc[500005];
map<int, int> f;
int pri[500005], cnt;
bool vi[500005], vv[500005];
void w() {
	for (int i = 2; i <= ma; i++) {
		if (!vi[i]) {
			pri[++cnt] = i;
			for (int j = 2; j * i <= ma; j++) {
				vi[i * j] = true;
			}
		}
	}
}
int main() {
	freopen("sor.in", "r", stdin);
	freopen("sor.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		vis[a[i]]++;
		ma = max(ma, a[i]);
	}
	w();
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j * i <= ma; j++) {
			if (vis[j * i]) {
				for (int k = 1; k <= vis[j * i]; k++) v[i].push_back(j * i);
				if (gc[i] == 0) gc[i] = j * i;
				else gc[i] = __gcd(gc[i], j * i);
				int pos = lower_bound(pri + 1, pri + 1 + cnt, j) - pri;
				if (pri[pos] == j) vv[i] = true;
			}
		}
	}
	for (int i = 1; i <= m; i++) {
		if (gc[i] != i) {
			cout << -1 << ' ' << -1 << '\n';
			continue;
		}
		if (vis[i]) {
			cout << 1 << ' ' << v[i].size() << '\n';
			continue;
		}
		if (vv[i]) {
			cout << 2 << ' ' << v[i].size() << '\n';
			continue;
		}
		f.clear();
		for (int j = 0; j < v[i].size(); j++) {
			int now = v[i][j];
			if (!f[now]) f[now] = 1;
			else f[now] = min(f[now], 1);
			for (auto it = f.begin(); it != f.end(); it++) {
				int u = __gcd(it -> first, now);
				if (u < i) continue;
				if (!f[u]) f[u] = it -> second + 1;
				else f[u] = min(f[u], it -> second + 1);
			}
		}
		cout << f[i] << ' ' << v[i].size() << '\n';
	}
	return 0;
}

对于正解,考虑到答案不会很大(小于等于 $ 7 $,考虑 $ 2 \times 3 \times 5 \times 7 \times 11 \times 13 \times 17 \times 19 = 9699690 > 600000 $ ),所以可以先枚举答案,然后判断是否合法;

以下设 $ w $ 为值域;

判断是否合法的问题可以转化为方案数,设 $ f_i $ 表示当前 $ lcm = i $ 的方案数,则有转移 $ f_i = C_{sum_i}^{t} - \sum_{j = 2}^{\lfloor \frac{w}{i} \rfloor} f_j $;

最后判断 $ f $ 是否为 $ 0 $ 即可,这里可以对一个比较大的质数取模(毕竟是这个质数的倍数的概率较低);

时间复杂度:$ \Theta(w \ln w) $;

点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const long long mod = 998244353;
int n, m;
int a[5000005], ma, vis[5000005];
int ans[5000005];
vector<int> v[5000005];
long long f[5000005], fac[5000005], fav[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;
	if (b < 0) return 0;
	return fac[a] * fav[b] % mod * fav[a - b] % mod;
}
int main() {
	freopen("sor.in", "r", stdin);
	freopen("sor.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		vis[a[i]]++;
		ma = max(ma, a[i]);
	}
	fac[0] = 1;
	fav[0] = 1;
	for (int i = 1; i <= 1000000; i++) {
		fac[i] = fac[i - 1] * i % mod;
		fav[i] = ksm(fac[i], mod - 2);
	}
	for (int i = 1; i <= ma; i++) {
		for (int j = 1; j * i <= ma; j++) {
			if (vis[j * i]) {
				for (int k = 1; k <= vis[j * i]; k++) v[i].push_back(j * i);
			}
		}
	}
	for (int t = 1; t <= 7; t++) {
		for (int i = 1; i <= ma; i++) f[i] = 0;
		for (int i = ma; i >= 1; i--) {
			if (v[i].size() < t) continue;
			long long sum = 0;
			for (int j = 2; j * i <= ma; j++) {
				sum = (sum + f[i * j]) % mod;
			}
			f[i] = (C(v[i].size(), t) - sum + mod) % mod;
		}
		for (int i = 1; i <= m; i++) {
			if (f[i] != 0 && !ans[i]) {
				ans[i] = t;
			}
		}
	}
	for (int i = 1; i <= m; i++) {
		if (!ans[i]) cout << -1 << ' ' << -1 << '\n';
		else cout << ans[i] << ' ' << v[i].size() << '\n';
	}
	return 0;
}

狗卡 0pts

赛时被 $ 600005 $ 个 deque 欺骗以致于都 RE,以此警告;

deque 的空间要乘 $ 16 $ 还是多少,所以谨慎;

对于这个题,我们首先想一想每个武将只有一级的时候,那么每次选最小即可;

如果不是一级,考虑两个数段 $ a, b $ ,$ a $ 先于 $ b $ 当 $ \overline{a} < \overline{b} $ 时;

所以我们找每个武将的最长递增平均值段即可,考虑插入一个数,我们让它一直和前面的段合并(注意这里是可行性合并,即能合并就一直合并)直到递增即可;

用个堆维护一下,每次出最小的,时间复杂度:$ \Theta(n \log n) $;

点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
long long n, m;
vector<int> v[600005];
struct sss{
	double val;
	int id, l, r;
	long long sum;
	bool operator <(const sss &A) const {
		return val > A.val;
	}
}e[1200005];
priority_queue<sss> q;
int main() {
	freopen("dog.in", "r", stdin);
	freopen("dog.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	int k, x;
	for (int i = 1; i <= n; i++) {
		cin >> k;
		v[i].push_back(0);
		for (int j = 1; j <= k; j++) {
			cin >> x;
			v[i].push_back(x);
		}
		int now = 0;
		for (int j = 1; j <= k; j++) {
			e[++now] = {1.00 * v[i][j], i, j, j, v[i][j]};
			while(now > 1 && e[now].val <= e[now - 1].val) {
				e[now - 1].r = e[now].r;
				e[now - 1].sum += e[now].sum;
				e[now - 1].val = 1.00 * e[now - 1].sum / (e[now - 1].r - e[now - 1].l + 1);
				now--;
			}
		}
		for (int j = 1; j <= now; j++) q.push(e[j]);
	}
	long long sum = 0, now = 0, ans = 0;
	while(!q.empty()) {
		sss t = q.top();
		q.pop();
		for (int i = t.l; i <= t.r; i++) {
			ans += sum * v[t.id][i];
			now += v[t.id][i];
			sum++;
		}
	}
	ans += (m - now) * sum;
	cout << ans;
	return 0;
}
posted @ 2024-11-18 15:18  Peppa_Even_Pig  阅读(38)  评论(1编辑  收藏  举报