[赛记] 多校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;
}