首页 > 其他分享 >[赛记] 【MX-S7】梦熊 NOIP 2024 模拟赛 3 && 2025炼石计划NOIP 模拟赛 #20

[赛记] 【MX-S7】梦熊 NOIP 2024 模拟赛 3 && 2025炼石计划NOIP 模拟赛 #20

时间:2024-11-25 10:44:29浏览次数:5  
标签:cnt 赛记 lc NOIP int sum long id 模拟

Happy Card 70pts

大样例乱搞都能过。。。

可以将“炸”看成“三带一”,那么我们最优是先出“三带一”;

首先分别算出原序列中每个数包含 $ 3 $ 的个数 $ cnt $ ,以及模 $ 3 $ 余 $ 1, 2 $ 的个数 $ s1, s2 $ ,然后进行判断, 如果 $ cnt \geq s1 + 2s2 $,那么我们可以看做将原序列所有数四个四个的出,看最后剩下几个,如果剩下 $ 1, 2 $ 个就再出一次,剩 $ 3 $ 个就再出两次;

如果 $ cnt < s1 + 2s2 $ ,那么我们优先带 $ 1 $ , 然后再带 $ 2 $ 即可;

时间复杂度: $ \Theta(n) $ , 瓶颈在输入;

点击查看代码
#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

考虑我们要求的这个点的位置有几种,有如下两种:

  1. 在这条链上以及这条链的子树内;

  2. 在 $ LCA $ 到根的这条链以及它们的子树内;

考虑对这两种情况分别求解,首先一个点 $ y $ 对点 $ x $ 的贡献为 $ a_y - 2dis(x, y) $,所以依据这个我们可以进行DP, 设 $ b_x $ 表示考虑以 $ x $ 为根的子树中到 $ x $ 的最大贡献是多少,有转移 $ b_{x} = \max(b_x, b_u - 2w) $,其中 $ w $ 为边权, $ u $ 为 $ x $ 的一个儿子,初始化 $ b_x = a_x $;

首先考虑如何处理 $ 1 $ ,求一条链上的最值很容易想到倍增,设 $ f_{x, i} $ 表示从 $ x $ 向上跳 $ 2^i $ 跳到哪里, $ g_{x, i} $ 表示从 $ x $ 向上跳 $ 2^i \ b $ 数组的最大值,直接转移即可;

对于 $ 2 $ , 我们类比上面的处理方法,设 $ w_{x, i} $ 表示从 $ x $ 开始向上 $ 2^i $ 的所有点以及它们的子树中的点到 $ i $ 的最大贡献,有转移 $ w_{x, i} = \max(w_{x, i - 1}, w_{f_{x, i - 1}, i - 1} - 2dis(x, f_{x, i - 1})) $;

注意上面我们并不用刻意的处理重复的子树,因为如果选中的点在重复的子树中,那么它会在深度最深的地方被选,在当前点被选已经没有什么意义了;

然后直接倍增处理询问即可;

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

点击查看代码
#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 操作,那么这个贡献是确定的,我们直接从前一个子序列找到对应的后缀和即可,但是此时有这个操作,其实就是找前面的子序列中有贡献的那些数的最大后缀和,然后和前面的贡献合并(取 $ \max $),这个可以上线段树前缀加区间查询做;

具体地,考虑设 $ f_i $ 表示只考虑前 $ i $ 位的答案,有转移 $ f_i = \max(f_{i - 1}, f_{i - 1} + \max_{l = pos_i}^{i - 1} \sum_{j = l}^{i - 1} a_j \times vis_j) $, 其中 $ pos_i $ 表示 $ a_i $ 前面第一个比它大的数, $ vis_j $ 表示 $ j $ 对于当前的 $ i $ 有没有贡献(就是 $ j $ 是否在 $ i $ 前面的子序列中且是否有 $ a_j < a_i $),后面这个式子(就是最大后缀和)可以线段树维护,和上面相同,只不过需要动态插入 $ f_i $ 以达到状态转移的效果;

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

点击查看代码
#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

考虑将一个复制后会跟着若干个粘贴,所以将赋值粘贴一起考虑,那么我们设这一次粘贴了 $ m $ 次,那么就是对原序列乘了一个 $ m + 1 $;

考虑设进行了 $ k $ 次操作( $ \Theta \log n $ 级别 ),那么我们的贡献为 $ \prod_i (m_i + 1) $,代价为 $ kx + y \sum_i m_i $,于是我们想在 $ \prod (m_i + 1) > n $ 的前提下使代价最小;

引理:$ m $ 数组中的数相差不超过 $ 1 $;

考虑证明,根据均值不等式,可以发现积定相等时和最小,但是都是整数,所以最多差 $ 1 $;

首先我们可以枚举 $ k $,然后考虑枚举较小的那个 $ m $ 的数量,然后二分出一个较小的 $ m $,最后直接循环判断一下是否满足 $ \prod (m_i + 1) > n $ 即可;

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

点击查看代码
#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

比较简单的数据结构题,不写了;

标签:cnt,赛记,lc,NOIP,int,sum,long,id,模拟
From: https://www.cnblogs.com/PeppaEvenPig/p/18567115

相关文章

  • XS9922B 同轴RX芯片 四通道 多合一模拟高清解码器用户指南
    1.1概述XS9922B是一款4通道模拟复合视频解码芯片,支持HDCCTV高清协议和CVBS标清协议,视频制式支持720P/1080P高清制式和960H/D1标清制式。芯片将接收到的高清模拟复合视频信号经过模数转化,视频解码以及2D图像处理之后,转化为YCbCr......
  • 【MX-S7】梦熊 NOIP 2024 模拟赛 3 & SMOI Round 2
    俩签到俩不可做是吧。Rank【MX-S7-T1】「SMOI-R2」HappyCard签到题一号,以为撑死评个黄但没想到那么多人不会打扑克。考虑炸弹也是三带一,出三带一肯定更优秀。考虑将所有牌变为若干个三张和剩余的,那么三张先带单张,再将对子拆开带。那么现在就有以下几种情况:单张太多,那么......
  • 蓝桥杯c++算法学习【5】之枚举与模拟(卡片、回文日期、赢球票、既约分数:::非常典型的比刷
     别忘了请点个赞+收藏+关注支持一下博主喵!!!! ! ! !!!关注博主,更多蓝桥杯nice题目静待更新:)枚举与模拟一、卡片:【问题描述】        小蓝有很多数字卡片,每张卡片上都是一个数字(0到9)。         小蓝准备用这些卡片来拼一些数,他想从1开始拼出正整数......
  • NOIP2016 提高组 换教室
    NOIP2016提高组换教室非常简单的一道期望dp,但是自己做的时候严重想复杂导致做了3天……算法一容易发现任意两次课之间转移的期望代价只和当前起终点的状态有关,因此每次转移可以独立出来了。现在想怎么算这个期望。一个结论:期望的计算:如果概率为\(k\)的代价为\(w_1\),概......
  • 【MX-S7】梦熊 NOIP 2024 模拟赛 3 & SMOI Round 2(同步赛)
    【MX-S7】梦熊NOIP2024模拟赛3&SMOIRound2(同步赛)\(T1\)luoguP11323【MX-S7-T1】「SMOI-R2」HappyCard\(20pts\)发现可以把「炸弹」也看做「三带一」。先使用「三带一」带走原用于出「单牌」的牌,若「三带一」还有剩余则尝试带走原用于出「对子」的牌,否则直接......
  • 天梯赛练习集 L2-041 插松枝 模拟
    #include<bits/stdc++.h>usingnamespacestd;queue<int>t,z;intx;voidprint(){ while(!z.empty()) { x=z.front();cout<<x; if(z.size()!=1) cout<<''; z.pop(); } cout<<endl;}intmain(){ intn,m,k; ci......
  • 折叠光腔衰荡高反射率测量技术的matlab模拟理论分析
    折叠光腔衰荡高反射率测量技术的matlab模拟理论分析1.前言2.光腔模型3.光腔衰荡过程4.衰荡时间与反射率的关系5.测量步骤①.光腔调节:②.光腔衰荡测量:③.计算衰荡时间常数:④.反射率计算:6.实际应用中的调整7.技术优势和局限8.总结9.其他情况的代码案例:①角......
  • 『模拟赛』【MX-S7】梦熊NOIP2024模拟赛3
    Rank因为提前知道打不完就没打暴力A.【MX-S7-T1】「SMOI-R2」HappyCardlink。签。比较好想到炸弹等价于三带一,因此本质只有三种出牌类型。并且三带一一次能出掉四张牌,显然优先打三带一是很优的。所以我们处理出三带的个数以及剩下零牌中对子的个数,然后分讨。如果三带......
  • string模拟
    7-1日期格式化分数5全屏浏览切换布局作者 陈越单位 浙江大学世界上不同国家有不同的写日期的习惯。比如美国人习惯写成“月-日-年”,而中国人习惯写成“年-月-日”。下面请你写个程序,自动把读入的美国格式的日期改写成中国习惯的日期。输入格式:输入在一行中按照“......
  • P1125 [NOIP2008 提高组] 笨小猴 C语言
    先说思路:创建了一个函数来判断是否是质数,然后将字符串输入,因为题干中说长度小于100,再加上\0,所以要把长度定义为101,之后对每一个字母用双层循环进行遍历,外层用count来计数,若超过maxn则赋新值,minn同样,之后再对maxn-minn得到的数进行判断即可,之后根据题意用if-else语句即可完成......