2 min read 522 words Updated Apr 25, 2026 Created May 03, 2026
##include

0718 测试题解

有一道原题,有点难受。所以 300。

出题人也是真厉害……不说了。

下发的题解非常的弱智,有很多地方几乎是错的……太难受了。

所以决定写这篇。

gnsi

(其实是sign,绝+1)

重心有一个结论:三点 -> (横坐标的平均值,纵坐标的平均值)

所以可以很轻易的找到上下界来判断。

quencese

傻逼计数

考虑递推的式子,设 $f_{t, i}$ 表示第 $t$ 次最后一个数是 $i$ 的可能数:

$$f_{t, i} = \sum_{j = 1}^{\lfloor \sqrt i \rfloor} f_{t + 1, i} $$

又由于实际上 $t$ 据说在 $10$ 次左右就只剩下了 $1$,也就是说,$\forall (t_1, t_2) (f_{t_1, i} = f_{t_2, i})$

所以只保留 $i$ 这一维,向上推即可。

这是 $O(n \sqrt n)$ 的,显然不够优秀,所以考虑优化。

看式子,长得像前缀和,于是利用前缀和优化:

$g_i = \sum_{j = 1}^{i} f_j$

于是有 $f_i = g_{\lfloor \sqrt i \rfloor}$

两者稍微合并一下:

$$g_i = sum_{j = 1}^{i} g_{\lfloor \sqrt j \rfloor} $$

很明显,这里可以用类似数论分块的方法求和即可。

大概的复杂度是:

$$T(n) = \sqrt n T(\sqrt n) + O(\sqrt n) $$

我不清楚这个东西复杂度到底是什么,但是很明显的是,非常的迅速,大概是在 $O(\sqrt n)$ 左右。

这是核心代码:

using lint = long long;
lint calc(lint x) {
    if (g.get(x))
        return g.get(x);
    if (x == 1) return 1;

    lint v = 0;
    for (lint i = 1; i * i <= x; ++i) {
        lint r = min(x + 1, (i + 1) * (i + 1));
        v += calc(i) * (r - i * i) % mod;
        v %= mod;
    }

    g.set(x, v);
    return v;
}

trolcon

弱智 DP 题,根本做不来。

很明显的结论是最多只需要合并三次,所以考虑用合并的次数作为状态之一,找到最小的支配集大小:

$f_{i, k}$ 表示考虑到第 $i$ 个线段,合并的 $k$ 次时,最小的支配集大小。

定义 $last_i$ 表示第 $i$ 条线段左侧右端点最大的线段(不相交)。

$conj_i$ 表示与第 $i$ 条线段相交的线段中左端点最小的线段。$conj^k_i$ 就是嵌套几层下去……

于是可以有转移方程:

$$f_{i, k} = \min{ f_{last_i, k} + 1, \min_{t = 0}^{k} (f_{conj^{t + 1}_i, k - t} + 1}) $$

看着头皮发麻 QwQ

那么问题在于如何维护 $last_i$$conj_i$……这可以用 set 进行维护。

考虑根据当前考虑的区间的左端点分成三个部分:完全在左边(按照右端点排序),包含了左端点(按照左端点排序),完全在右边。

然后类似于单调栈的思路不断弹出即可。

#include <iostream>
#include <algorithm>
#include <vector>
#include <array>
#include <set>

using namespace std;
const int N = 1e5 + 7;

struct LR {
	int l, r, i;
} segs[N];

struct cmpByR {
	bool operator() (const LR &lhs, const LR &rhs) {
		return lhs.r > rhs.r;
	}
}; 

struct cmpByL {
	bool operator() (const LR &lhs, const LR &rhs) {
		return lhs.l < rhs.l;
	}
};

set<LR, cmpByL> all, rs, ms;
set<LR, cmpByR> ls;

const int M = N * 30, INF = 1e9 + 7;

int last[N], conj[N];
int f[N][4];

int main() {
	freopen("trolcon.in", "r", stdin);
	freopen("trolcon.out", "w", stdout);
	
	cin.tie(0)->sync_with_stdio(false);
	
	int n; cin >> n;
	for (int l, r, i = 1; i <= n; ++i) {
		cin >> l >> r;
		all.insert({l, r, i});
		rs.insert({l, r, i});
	}

	int maxk = 0, ending = 1;
	for (auto it : all) {
		int x = it.l;
		while (!ms.empty() && ms.begin()->r < x) {
			ls.insert(*ms.begin());
			ms.erase(ms.begin());
		}
		
		while (!rs.empty() && rs.begin()->l <= x) {
			ms.insert(*rs.begin());
			rs.erase(rs.begin());
		}
		
		int i = ending = it.i, lst = ls.size() ? ls.begin()->i : 0, cont = ms.size() ? ms.begin()->i : 0;
		last[i] = lst, conj[i] = cont;
		for (int t = 0; t <= 3; ++t) {
			f[i][t] = f[lst][t] + 1;
			for (int k = 0, con = cont; k <= t; ++k, con = conj[con]) {
				f[i][t] = min(f[i][t], f[last[con]][t - k] + 1);
			}
			
			maxk = max(maxk, f[i][t]);
		}
	}
	
	if (maxk <= 1)
		return cout << "-1" << '\n', 0;
	for (int k = 1; k <= 3; ++k) {
		if (f[ending][k] < f[ending][0]) {
			cout << k << '\n';
			return 0;
		}
	}
	
	cout << "ROCKYOU" << '\n';
	return 0;
}

doog

利用矩阵有结合律以及没有交换律,以及矩阵的逆的性质即可。