2024.1.20 模拟赛总结

发布时间:2024年01月20日

总结

时间安排

8 : 00 ~ 10 : 00 \qquad 8:00\sim 10:00 8:0010:00 T 1 T1 T1。第一眼看上去并没有明确的思路,简单想了想发现可以直接贪心走,不过好像有点小细节。推了推细节发现还是用线段树维护写起来顺一点。 9 : 40 9:40 9:40 写完并调完第一发。不过他题目给的 n = 5 × 1 0 5 n=5\times 10^5 n=5×105,而我用线段树的频率异常之高,害怕被卡常就测了一发极限数据。一测发现线段树有一点写挂了!给的大样例压根就没测出来……简单一改加个快读就跳了。

10 : 00 ~ 10 : 30 \qquad 10:00\sim 10:30 10:0010:30 T 2 T2 T2。一眼看上去感觉 T 2 T2 T2 并不难,很快想到了一个莫队计算区间出现数字种类数的做法,但是 n = 5 × 1 0 5 n=5\times 10^5 n=5×105。总感觉这个问题很典,但是并没想出来怎么做。中间想过主席树,不过也没想出来咋用。于是乎就先把莫队写了,拿个 60 p t s 60pts 60pts

10 : 30 ~ 10 : 50 \qquad 10:30\sim 10:50 10:3010:50 T 3 T3 T3。看到极差就觉得肯定是个套路题,但是不知道极差有啥套路……开始想二分极差,但是发现搞不了。接着就想二分最大值,然后 d p dp dp 出来最小值最大能是多少。不过细想了一下发现二分好像不是很对,好像得三分。凭着感觉写完了三分套 d p dp dp,调了一小会过了样例就没再管。

10 : 50 ~ 11 : 50 \qquad 10:50\sim 11:50 10:5011:50 T 4 T4 T4 T 4 T4 T4 也能看出来是套路题(指统计贡献),但是感觉太麻烦了,而且无思路,就开始想暴力。 O ( n 3 ) O(n^3) O(n3) 很好搞,不过并没有这一档部分分!开始想怎么搞 O ( n 2 ) O(n^2) O(n2)。口胡了一个枚举根,在遍历树的时候顺带计算一下答案。感觉很对就写了,写完就发现寄了。很多情况没考虑到。缝缝补补调了一大晌,手玩样例发现这个做法好像就不对……然后就罚坐到了比赛结束。

考试结果

95 + 60 + 100 + 0 = 255 p t s \qquad 95 + 60 + 100 + 0 = 255pts 95+60+100+0=255pts, R a n k : 1 Rank:1 Rank:1(虽然只有两个人)。 T 1 T1 T1 线段树终究被卡常了……

考试总结

\qquad 意识到 T 1 T1 T1 可能被卡常后就应该再深入剖析一下线段树做的操作是什么。维护 S u m Sum Sum 是单点修改,区间查询,维护 D a y Day Day 是区间修改单点查询,这两者都可以用常数更小的树状数组来维护。而对于 D a t Dat Dat 完全可以用 S T ST ST 表代替。

\qquad T 2 T2 T2 确实很,树状数组就能搞。以前的一些知识、套路还是掌握的不牢固。

题解

T1

种太阳

\qquad 一个显而易见的性质:如果遇到了一个会让小 P P P 破产的点 i i i,那么我们一定会在 1 ~ i 1\sim i 1i 中收益最大的点积攒经济直到可以通过点 i i i。模拟上述过程即可。可以用树状数组搞到 O ( n log ? n ) O(n\log n) O(nlogn),也可以直接用一个变量来储存偏移量,因为我们只会用到上一个位置和前缀最值的信息。前缀最值预处理一下即可,整体复杂度就可以降至 O ( n ) O(n) O(n)

T2

储存管理

\qquad 我们显然不能直接枚举 k k k,考虑我们可以怎样给 k k k 贡献答案。对于一个位置 i i i,如果 a i a_i ai? 之前没有出现过,那么我们要给所有的 k k k 贡献 1 1 1 的插入次数。如果 a i a_i ai? 之前出现过,记上一次出现过的位置为 l s t i lst_i lsti?。假设 ( l s t i , i ) (lst_i,i) (lsti?,i) 之间的数字种类数为 n u m num num,那么我们会对所有 k ≤ n u m k\leq num knum k k k 贡献 1 1 1 的插入次数。证明显然。这样我们就把问题转化为:一共有 m m m 个形如 [ l s t i + 1 , i ? 1 ] [lst_i+1,i-1] [lsti?+1,i?1] 的区间,对于每一个区间,求出区间内出现过的数字的种类数。假设种类数为 n u m num num,那么我们让 s n u m s_{num} snum? 加一,最后求一个 s s s 的后缀和就是答案。现在,我们考虑如何快速求一段区间内出现过的数字的种类数。

\qquad 这个问题是不是很熟悉?我们将每个区间挂在区间右端点处,然后从左到右扫区间右端点。对于点 i i i,我们在树状数组上让 l s t i lst_i lsti? 减一,让 i i i 加一,对于每一个区间我们求区间和即可。时间复杂度 O ( n log ? n ) O(n\log n) O(nlogn)

\qquad 核心 C o d e : Code: Code:

for(int i = 1; i <= m; i ++) {
	if(lst[i]) T.add(lst[i], -1);
	T.add(i, 1);
	int Now = T.ask(i);
	for(auto x : pos[i]) {
		int ans = T.ask(x - 1);
		s[Now - ans] ++;
	}
}

T3

划分区间

\qquad 直接维护最小极差是不好维护的。二分极差后 c h e c k check check 也是非常难搞的。但是我们注意到,如果知道当前集合中的最大值,那么我们只要尽可能地让最小值最大,就可以最小化极差。所以我们可以考虑二分最大值……吗?

\qquad 直接二分是不行的,因为如果最大值过大,大到序列中任何两个数都拼不出这个最大值,那这个最大值也一定是不优的。所以我们考虑三分最大值。(不过在这想道题的过程中,我突然发现三分也可以求出二分的最优值!好愚蠢地发现啊)最大值固定后,让最小值最大就好搞了,简单 d p dp dp 即可。设 d p i , 0 / 1 / 2 dp_{i,0/1/2} dpi,0/1/2? 表示第 i i i 个数 ? \, 未与前面结合,不新开一段/未与前面结合,新开一段/与前面结合,新开一段 ? \, 能得到的最小值。转移很好转。

\qquad 核心 C o d e : Code: Code:

int check(int x) {
	memset(dp, 0x7f, sizeof dp);
	for(int i = 1; i <= n; i ++) {
		if(a[i] + a[i - 1] <= x) dp[i][2] = min(dp[i - 1][0], a[i] + a[i - 1]);//能与前面结合
		if(dp[i - 1][2] != 0x7f7f7f7f) dp[i][1] = min(max(dp[i - 1][1], dp[i - 1][2]), a[i]);//新开一段
		else dp[i][1] = min(dp[i - 1][1], a[i]);
		if(dp[i - 1][2] != 0x7f7f7f7f) dp[i][0] = max(dp[i - 1][1], dp[i - 1][2]);//不新开一段
		else dp[i][0] = dp[i - 1][1];
	}
	//由于dp[n][0]并没有将n归到某一段内,所以是一个不合法的状态
	if(dp[n][2] == 0x7f7f7f7f) return x - dp[n][1];
	return x - max(dp[n][1], dp[n][2]);
}

T4

圣诞树

\qquad 还不会……

文章来源:https://blog.csdn.net/x052xiao/article/details/135715729
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。