[赛记] 【MX-S7】梦熊 NOIP 2024 模拟赛 3 && 2025炼石计划NOIP 模拟赛 #20
Happy Card 70pts
大样例乱搞都能过。。。
可以将“炸”看成“三带一”,那么我们最优是先出“三带一”;
首先分别算出原序列中每个数包含
如果
时间复杂度:
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int t;
int n;
long long a[500005];
long long s1, s2, sum, cnt, ans;
int main() {
// freopen("in.in", "r", stdin);
// freopen("out.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> t;
while(t--) {
cin >> n;
ans = 0;
s1 = s2 = sum = cnt = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (a[i] % 3 == 1) s1++;
if (a[i] % 3 == 2) s2++;
cnt += (a[i] / 3);
sum += a[i];
}
if (cnt >= s1 + 2 * s2) {
ans = sum / 4;
if (sum % 4 == 1) ans++;
else if (sum % 4 == 2) ans++;
else if (sum % 4 == 3) ans += 2;
cout << ans << '\n';
} else {
ans = cnt;
long long s = s1;
s1 -= min(s, ans);
cnt -= min(s, ans);
s2 -= min(s2 * 2, cnt) / 2;
cout << ans + s1 + s2 << '\n';
}
}
return 0;
}
Speaker 100pts
考虑我们要求的这个点的位置有几种,有如下两种:
-
在这条链上以及这条链的子树内;
-
在
到根的这条链以及它们的子树内;
考虑对这两种情况分别求解,首先一个点
首先考虑如何处理
对于
注意上面我们并不用刻意的处理重复的子树,因为如果选中的点在重复的子树中,那么它会在深度最深的地方被选,在当前点被选已经没有什么意义了;
然后直接倍增处理询问即可;
时空复杂度:
点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int n, q;
long long a[500005];
struct sss{
int t, ne;
long long w;
}e[500005];
int h[500005], cnt;
inline void add(int u, int v, long long ww) {
e[++cnt].t = v;
e[cnt].ne = h[u];
h[u] = cnt;
e[cnt].w = ww;
}
int f[200005][21], dep[200005];
long long w[200005][21], g[200005][21], dis[200005], b[200005];
void afs(int x, int fa) {
b[x] = a[x];
for (int i = h[x]; i; i = e[i].ne) {
int u = e[i].t;
if (u == fa) continue;
afs(u, x);
b[x] = max(b[x], b[u] - 2 * e[i].w);
}
}
void dfs(int x, int fa) {
dep[x] = dep[fa] + 1;
f[x][0] = fa;
g[x][0] = max(b[x], b[fa]);
for (int i = h[x]; i; i = e[i].ne) {
int u = e[i].t;
if (u == fa) continue;
w[u][0] = b[x] - 2 * e[i].w;
dis[u] = dis[x] + e[i].w;
dfs(u, x);
}
}
inline int lca(int x, int y) {
if (x == y) return x;
if (dep[x] < dep[y]) swap(x, y);
for (int i = 19; i >= 0; i--) {
if (dep[f[x][i]] >= dep[y]) x = f[x][i];
}
if (x == y) return x;
for (int i = 19; i >= 0; i--) {
if (f[x][i] != f[y][i]) {
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
}
inline long long di(int x, int y, int lc) {
return dis[x] + dis[y] - 2 * dis[lc];
}
inline long long W(int x, int y, int lc) {
long long ma = -1e18;
int xx = x, yy = y;
for (int i = 19; i >= 0; i--) {
if (dep[f[x][i]] >= dep[lc]) {
ma = max(ma, g[x][i]);
x = f[x][i];
}
}
for (int i = 19; i >= 0; i--) {
if (dep[f[y][i]] >= dep[lc]) {
ma = max(ma, g[y][i]);
y = f[y][i];
}
}
if (xx == yy) ma = b[xx];
int now = lc;
for (int i = 19; i >= 0; i--) {
if (dep[f[lc][i]] >= dep[1]) {
ma = max(ma, w[lc][i] - 2 * di(lc, now, lc));
lc = f[lc][i];
}
}
return ma;
}
int main() {
// freopen("in.in", "r", stdin);
// freopen("out.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 >> a[i];
}
int x, y;
long long ww;
for (int i = 1; i <= n - 1; i++) {
cin >> x >> y;
cin >> ww;
add(x, y, ww);
add(y, x, ww);
}
a[0] = -1e18;
b[0] = -1e18;
afs(1, 0);
dfs(1, 0);
for (int j = 1; j <= 19; j++) {
for (int i = 1; i <= n; i++) {
f[i][j] = f[f[i][j - 1]][j - 1];
g[i][j] = max(g[i][j - 1], g[f[i][j - 1]][j - 1]);
w[i][j] = max(w[i][j - 1], w[f[i][j - 1]][j - 1] - 2 * di(i, f[i][j - 1], f[i][j - 1]));
}
}
long long ans = 0;
for (int i = 1; i <= q; i++) {
cin >> x >> y;
int lc = lca(x, y);
ans = a[x] + a[y] - di(x, y, lc) + W(x, y, lc);
cout << ans << '\n';
}
return 0;
}
Monotonic Queue 4pts
首先考虑转化题意,我们发现,我们可以将序列划分成若干个单调递减的子序列,然后只有一个子序列的开头能够弹出另一个子序列的末尾的若干个元素,这个子序列的划分可以用单调栈预处理;
考虑我们的贡献会出现在子序列的开头,剩下的数弹不出其它数没有意义,发现这个性质后我们就可以发现,这个贡献是前一段子序列的一个后缀和;
考虑如果没有 pop_front
操作,那么这个贡献是确定的,我们直接从前一个子序列找到对应的后缀和即可,但是此时有这个操作,其实就是找前面的子序列中有贡献的那些数的最大后缀和,然后和前面的贡献合并(取
具体地,考虑设
时间复杂度:
点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
int n;
long long c[500005], a[500005];
namespace SEG{
inline int ls(int x) {
return x << 1;
}
inline int rs(int x) {
return x << 1 | 1;
}
struct sss{
int l, r;
long long ma, lz;
}tr[3000005];
inline void push_down(int id) {
if (tr[id].lz != 0) {
tr[ls(id)].lz += tr[id].lz;
tr[rs(id)].lz += tr[id].lz;
tr[ls(id)].ma += tr[id].lz;
tr[rs(id)].ma += tr[id].lz;
tr[id].lz = 0;
}
}
inline void push_up(int id) {
tr[id].ma = max(tr[ls(id)].ma, tr[rs(id)].ma);
}
void bt(int id, int l, int r) {
tr[id].l = l;
tr[id].r = r;
if (l == r) return;
int mid = (l + r) >> 1;
bt(ls(id), l, mid);
bt(rs(id), mid + 1, r);
}
void add(int id, int l, int r, long long d) {
if (tr[id].l >= l && tr[id].r <= r) {
tr[id].ma += d;
tr[id].lz += d;
return;
}
push_down(id);
int mid = (tr[id].l + tr[id].r) >> 1;
if (l <= mid) add(ls(id), l, r, d);
if (r > mid) add(rs(id), l, r, d);
push_up(id);
}
long long ask(int id, int l, int r) {
if (tr[id].l >= l && tr[id].r <= r) return tr[id].ma;
push_down(id);
int mid = (tr[id].l + tr[id].r) >> 1;
if (r <= mid) return ask(ls(id), l, r);
else if (l > mid) return ask(rs(id), l, r);
else return max(ask(ls(id), l, mid), ask(rs(id), mid + 1, r));
}
}
int s[500005], cnt;
vector<int> v[500005];
int main() {
// freopen("queue6.in", "r", stdin);
// freopen("out.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> c[i];
}
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
SEG::bt(1, 1, n);
for (int i = 1; i <= n; i++) {
while(cnt > 0 && a[s[cnt]] < a[i]) v[i].push_back(s[cnt--]);
s[++cnt] = i;
}
long long ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < v[i].size(); j++) SEG::add(1, 1, v[i][j], c[v[i][j]]);
if (i == 1) continue;
long long f = SEG::ask(1, 1, i - 1);
ans = max(ans, f);
SEG::add(1, i, i, f);
}
cout << ans;
return 0;
}
邻间的骰子之舞 45pts
考虑将一个复制后会跟着若干个粘贴,所以将赋值粘贴一起考虑,那么我们设这一次粘贴了
考虑设进行了
引理:
数组中的数相差不超过 ;
考虑证明,根据均值不等式,可以发现积定相等时和最小,但是都是整数,所以最多差
首先我们可以枚举
时间复杂度:
点击查看代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
long long n, x, y;
long long ma;
unsigned __int128 ans;
void out(unsigned __int128 x) {
if (x == 0) return;
out(x / 10);
cout << (int)(x % 10);
}
int main() {
freopen("dice.in", "r", stdin);
freopen("dice.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> x >> y;
ma = log2(n) + 1;
ans = 1e36;
for (int k = 1; k <= ma; k++) {
for (int p = 0; p <= k; p++) {
long long l = 1;
long long r = n;
unsigned __int128 sum = 1;
long long t = n + 1;
while(l <= r) {
long long mid = (l + r) >> 1;
sum = 1;
for (int i = 1; i <= p; i++) {
if (sum > n) break;
sum *= mid;
}
for (int i = 1; i <= k - p; i++) {
if (sum > n) break;
sum *= (mid + 1);
}
if (sum > n) {
t = mid;
r = mid - 1;
} else l = mid + 1;
}
sum = 0;
for (int i = 1; i <= p; i++) sum += t;
for (int i = 1; i <= k - p; i++) sum += (t + 1);
unsigned __int128 an = (x - y) * k + y * sum;
ans = min(ans, an);
}
}
if (ans == 0) cout << 0;
else out(ans);
return 0;
}
星海浮沉录 100pts
比较简单的数据结构题,不写了;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端