把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

CSP-S2024 题目解析

CSP-S2024 题目解析

T1

弱智题目,没什么好讲的了,直接桶排+扫一遍统计答案即可。

#include <iostream>
using namespace std;
#define N 100005
int n,a[N],tot[N],mn = 2e9,mx,ans;
signed main(){
    cin >>n;
    for (int i = 1;i <= n;i ++) cin >> a[i],tot[a[i]] ++,mn = min(mn,a[i]),mx = max(mx,a[i]);
    int sum = tot[mn];
    for (int i = mn + 1;i <= mx;i ++) {
        if(!tot[i]) continue;
        int t = min(sum,tot[i]);
        sum -= t;
        sum += tot[i];
        ans += t;
    }
    cout << n - ans;
    return 0;
}

T2

分类讨论+二分+最小点覆盖区间问题。

  • \(a = 0\)\(v_i>V\)
    • 能捕捉到的摄像头 \([pos,m]\)\(pos\) 为第一个 \(p_k\) 能捕捉到其超速的摄像头编号。
  • \(a>0\)
    • 范围 \([pos,m].\)
  • \(a < 0\)
    • 范围 \([pos,x]\)\(x\) 为最后一个 \(p_k\) 能捕捉到其超速的摄像头编号。

然后我们就产生了 \(n\) 个区间,利用 最小点覆盖区间 解决 \(ans2\)\(ans1\) 可以通过 \(\mathcal{O}(n)\) 扫一遍解决。

T3

咱们不难想到一段一段的红蓝相间一定不劣于单个单个的红蓝相间

因此设 \(f_{i,j}\) 表示当前到 \(i\),最靠近 \(a_i\) 且颜色与其不同的数是 \(j.\)

不难的转移:

  • \(a_i = a_{i-1}.\)
    • \(f_{i,j}=f_{i-1,j}+a_i.\)
  • 否则:
    • \(f_{i,a_{i-1}} = \max_{k\in[1,i-1],j=a_k} f_{i-1,j}+[a_i=j]\times j.\)

考虑到每次转移只和 \(i-1\) 有关,直接删掉第一维。

得到 :

  • 第一种情况:\(f_j=f_j+a_i.\)
  • 第二种情况:\(f_{i-1}=f_j+[i=j]\times j.\)

因此空间复杂度为 \(\mathcal{O}(m)\) 的。

我们考虑优化。

由于第一种情况是 \(\mathcal{O}(1)\) 的,看看第二种情况的 \(\mathcal{O}(m).\)

对于 \([i=j]\times j\),实际上是 \(f_{i-1}=f_i+i\),这种情况一定是不劣于再到 \(i\) 前面找一个 \(a_k\) 使得 \(a_k=i.\)

那么其他的情况:其实就是 \(f_j.\)

那么我们对当前的 \(f\) 取个 \(max\) 不就行了,记为 \(\text{Max}.\)

第二种情况不难表达为 \(f_{i-1}=\max\{\text{Max},f_i+i\}.\)

因此两个都是 \(\mathcal{O}(1)\) 的,并且第一种情况可以用变量求出。

代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stdlib.h>
#define int long long
#define N 200005
#define M 1000006
using namespace std;
int f[M],n,a[N];
signed main(){
	int T;
	cin >> T;
	for (;T--;) {
		cin >> n;
		for (int i = 1;i <= n;i ++) cin >> a[i],f[a[i]] = -1e18;
		int Max = 0,ans = 0;
		for (int i = 2;i <= n;i ++) {
			if (a[i] == a[i - 1]) ans += a[i];
			else f[a[i - 1]] = max(Max,f[a[i]] + a[i]),Max = max(f[a[i - 1]],Max);
		}
		cout << Max + ans << endl;
	} 
	return 0;
}

T4

我们遇到此题可以考虑一步一步慢慢思考,一开始就想线性如果 \(1h\) 没想出来是对信心产生了极大的打击的。

除了最暴力的暴力算法

时间复杂度 \(\mathcal{O}(Tnm\log n)\),期望可得 \(40\)(赛场想到了类似的算法,但是最后没有调出来)

不难想到从单个去看能否达到加入贡献是一种不错的实现,这是一种化总为单的思想。

\(p=c_i\),以及要补充的选手为未知选手

对于每一个未知选手,为了让它有可能达到胜利的条件,都应该使其的能力值为 \(\text{max}.\)

我们用以下的方式讨论选手类型:

  • \(i\leq p\),那么其为已知选手
  • \(i>p\),那么其为未知选手

考虑模拟选手 \(i\) 成为胜利者(或不成为)的整个过程(下述过程中若有一处不满足条件,选手 \(i\) 将不能成为最后的赢家):

  • \(i\) 作为擂主,直接判断是否 \(a_i>R.\)
  • \(i\) 不作为擂主时,很难判断(我就是因此没有调出来),要分类讨论:
    • 如果在这次比赛前的一次比赛的另一边胜者 \(v\) 不是未知选手,那么直接判断即可。
    • 否则选手 \(i\) 直接失败。

代码有点难写,不过加上预处理这个时间复杂度是可观的,性价比较高。

优化暴力算法

时间复杂度 \(\mathcal{O}(T(n\log n+m))\) ,期望可得 \(76\) 分。

这样的单独处理有一些重复,为什么呢?因为 \(c_i\) 的答案符合单调性,若有 \(c_i<c_j\) 则在 \(c_j\) 中满足条件的选手 \(x(x\leq c_i)\) 必然可以在 \(c_i\) 中成为胜者。

不妨把整一个过程看成一颗二叉树,设 \(u\) 表示其根节点。

\(f_u,g_u\) 分别表示在 \(u\) 处的胜者以及想要一个未知选手成为 \(u\) 处的胜者,\(p\) 最大是多少。

\(depmax\) 为最多的轮数。

我们称一条从根节点一直往左子树走的路径为胜利路径,因为只有走到这个路径上的一点 \(u\),才表明能够在第 \(depmax-dep_u\) 轮成为胜利者。

转移请仔细认真理解。

我们考虑每次从 \(i\) 向上跳,维护一个 \(\max p\),跳到一个 \(u\)

  • 选手 \(i\) 作为擂主,判断:
    • \(a_i<R\),下播。
  • 否则,判断:
    • \(a_{brother_{fa_u}}\) 是否满足条件:
      • 是,\(\max p\leftarrow \min\{p,g_{borther_{fa_u}}\}.\)

若最终 \(i\) 走到胜利路径,则贡献一个范围(需要与选手类型区分,这是细节)与 \([1,\max p]\) 取交集,这个范围指的是在哪一些人之前(这就是个区间!)可以成为总场的胜利者

发现是一个区间,考虑查分优化。

单组数据处理 \(\mathcal{O}(n)\),计算答案时间复杂度 \(\mathcal{O}(n\log n+m).\)

\(\log\) 消失之术——正解

时间复杂度 \(\mathcal{O}(T(n+m))\),期望得分 \(100\) 分。

按照原来的思路

我们发现一个重要的优化条件,即:进行加法的区间个数只有 \(\mathcal{O}(n)\)

为什么呢?

因为作为在胜利路径上的点,这样的节点的子树深度恰好为 \([1,depmax]\) 各一个!而一个深度为 \(x\) 的胜利路径上的点 \(u\) 会有叶子结点数量这么多个区间贡献给它,而不难发现 \(\mathcal{O}(\sum_{k\in[1,depmax]} 2^k)=\mathcal{O}(n).\)

大胆一点,考虑对这 \(depmax\) 个子树全部遍历一次!

注意到:对于一个节点 \(i\) 有两点:

  • 会不会其作为擂主直接痛失资格。
  • 其对手能不能守擂成功,若可以向他的对手的 \(g\) 取最小值。

不难发现:后面的那个东西\(a_i\) 没有关系

而前者也是比较好处理的。

具体地,我们考虑将上述做法反转过来,自上而下地处理贡献。

考虑维护一个 \((x,y)\),经过一条边 \(u\rightarrow v\) 时,我们考虑枚举 \(i\)\(v\rightarrow u\) 的过程:

  • 如果 \(v\) 子树里面的点到 \(u\) 作为擂主,则将 \(x\) 向当前的 \(depmax\)\(\max.\)
  • 如果 \(v\)\(u\) 不作为擂主,判断对面是否守擂成功,可以则将 \(u\)\(g_{brother_v}\)\(\min.\)

当我们到达了叶子结点的时候(也就是我们的 \(i\)),此时 \(x\) 就可以判断 \(i\) 在往上跳的时候能不能达到我们枚举胜利路径上的点。

\(y\) 就可以描述贡献区间的约束。

时间复杂度 \(\mathcal{O}(n+m)\),常数较大,很多细节,不好实现。

另一种线段树思想

posted @ 2024-11-03 20:58  high_skyy  阅读(29)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end