2025 暑期 mx 集训 7.27
T1
https://www.mxoj.net/problem/P110066?contestId=79
题意
\(4\) 种括号,(),[],{},<>。
给你一个字符串,由这 \(4\) 种括号和 ? 组成。? 可以变成任意一个字符。
你要求有多少种方案使得字符串是合法的括号序列。
\(n\leq 30\)。
Solution
很典(但我赛后才知道很典)。
考虑区间 dp。设 \(f(l,r)\) 表示区间 \([l,r]\) 的方案数。
初始化 \(f(i, i - 1) = 1\)。
然后就做完了。时间复杂度 \(O(n^3)\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 40, inf = 0x3f3f3f3f;
int n, b[N], c[N];
string s;
char a[10][2];
ll f[N][N];
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
cin >> s; n = s.size();
s = " " + s;
a[1][0] = '(', a[1][1] = ')';
a[2][0] = '[', a[2][1] = ']';
a[3][0] = '{', a[3][1] = '}';
a[4][0] = '<', a[4][1] = '>';
for (int i = 1; i <= n; i++)
for (int j = 1; j <= 4; j++)
for (int k = 0; k < 2; k++)
if (s[i] == a[j][k]) b[i] = j, c[i] = k;
auto val = [&](int i, int j) -> ll {
int ans = 0;
if (b[i] == b[j] && !c[i] && c[j]) ans = 1;
if (b[i] != 0 && !c[i] && b[j] == 0) ans = 1;
if (!b[i] && b[j] && c[j]) ans = 1;
if (b[i] == 0 && b[j] == 0) ans = 4;
return ans;
};
for (int i = 1; i <= n + 1; i++) f[i][i - 1] = 1;
for (int l = 2; l <= n; l++) {
if (l % 2) continue;
for (int i = 1, j = i + l - 1; j <= n; i++, j++) {
for (int k = i; k <= j; k++)
f[i][j] += val(i, k) * f[i + 1][k - 1] * f[k + 1][j];
}
}
cout << f[1][n];
return 0;
}
T2
https://www.mxoj.net/problem/P110067?contestId=79
题意
有 \(n\) 个数,第 \(i\) 个数的取值范围为 \([l_i, r_i]\)。
求最长非降连续子序列。
\(n \leq 10^6, -10^9\leq l_i\leq r_i \leq 10^9\)。
Solution
首先考虑固定左端点,这个最长的如何求。
那么考虑左端点一定希望尽可能小,那肯定是选择 \(l\)。
然后接下来这个数肯定只能选 \([\max(l_{i - 1}, l_i), r_i]\) 了。
然后如果这个区间没了,那就不能选了。
接下来考虑移动左端点。
那随着左端点的后移,这个 \(\max(l_i)\) 肯定是不升的,所以对于你 \(i - 1\) 选的那些数,在 \(i\) 这同样符合。
所以这个满足单调性,直接双指针。然后还需要求区间 \(l_i\) 的 \(\max\),这个随便维护。
时间复杂度 \(O(n \log n)\),可以单调队列做到线性。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10, inf = 0x3f3f3f3f;
int n, x[N], y[N], tr[N << 2];
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define mid (l + r) / 2
void push_up(int rt) { tr[rt] = max(tr[lson], tr[rson]); }
void build(int rt, int l, int r)
{
if (l == r) return tr[rt] = x[l], void();
build(lson, l, mid), build(rson, mid + 1, r);
push_up(rt);
}
int query(int rt, int l, int r, int sp, int ep)
{
if (sp <= l && r <= ep) return tr[rt];
int res = -inf;
if (sp <= mid) res = query(lson, l, mid, sp, ep);
if (ep > mid) res = max(res, query(rson, mid + 1, r, sp, ep));
return res;
}
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; i++) cin >> x[i] >> y[i];
build(1, 1, n);
int ans = 1;
for (int i = 1, j = 1; i <= n; i++) {
while (j + 1 <= n) {
if (y[j + 1] >= query(1, 1, n, i, j)) j++;
else break;
ans = max(ans, j - i + 1);
}
}
cout << ans;
return 0;
}
T3
https://www.mxoj.net/problem/P110068?contestId=79
题意
给你一个 \(n\) 个点的内向树(除了根每个点恰好有一条出边,即每个点向根的方向连),每个点有 \(a_i\) 个人。
初始所有边都是 \(0\),你可以选择连续的一条路径,满足 \(b_{i} \to b_{i + 1}\) 然后删除中间这些边,并添加一条 \(b_1 \to b_m\) 的 \(1\) 边。
最终求 \((a, b)\) 对数的最小值,满足 \(a,b\) 是不同的两个人,且他们位于不同的点上,能够通过若干条边从 \(a\) 到达 \(b\)。
\(n \leq 10^5, a_i \leq 10^6\)。
Solution
所有边一定不交(端点处可以重合)。
首先考虑我们所有的 \(0\) 边都会替换成 \(1\) 边。
然后每个点的子树内只能有一个点是向上传递的,就是每个点的所有儿子之中,只有其中一个儿子里的点会传到父亲之上,其余的都会连到父亲这里。
所以考虑 \(f(u, i)\) 表示以 \(u\) 为根的子树中 \(i\) 向上传,子树内其余点的最小贡献。
转移考虑枚举这个 \(i\)。(\(\text{sub}(i)\) 表示 \(i\) 的子树)
然后第二个式子显然是 \(O(n)\) 的,第一个式子因为对于 \(i\) 只有一个 \(v\) 是符合要求的,然后后面这个 \(\min\) 是可以预处理的,所以也是 \(O(n)\) 的。
总时间复杂度 \(O(n^2)\)。有 \(75\) 分。
然后考虑对于一个 \(j\) 看作一个直线:\(a_j x + f(w, j)\)。然后李超树 + 线段树合并维护。
这个我不会,鸽了。
然后就是还有个问题,我把 \(f(u, u)\) 的转移写了就不对,要去掉,并且所有 \(f\) 初始化为 \(inf\),只有叶子才 \(f(u, u) = 0\)。
然后就对了,这个我不太理解。我写出来的错误代码和答案比较是小了的,但是这个写法不应该小。这个写法由于你非叶子点向上传,一定有 \(u \to v, v \to w\) 这种结构,但是你把他直接 \(u \to w\) 是更优的。所以不应该小,但是不懂啊。
ok,因为上边那个东西绝对劣,所以直接不写那个转移就行。然后为啥不对呢,因为这样的话你 \(u\) 下边连向 \(u\) 的边会通过 \(u\) 往上传的这条边连到上边的点。
比如 \(1\to 2, 2\to 3, 2\to 4\),这样你 \(2\) 向上传,那么 \(3,4\) 一定连向 \(2\) 了,然后他们可以通过 \(2\) 去到 \(1\),所以 \((a_3 + a_4) \times a_1\) 这个贡献是漏了的。所以不对。
Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e3 + 10;
int n, L[N], R[N], cnt;
vector<int> e[N];
int a[N], f[N][N], b[N], mn[N], s[N];
void dfs(int u)
{
L[u] = ++cnt; b[cnt] = a[u];
int res = 0;
for (auto v : e[u]) {
dfs(v);
mn[v] = 1e18;
for (int j = L[v]; j <= R[v]; j++) mn[v] = min(mn[v], f[v][j] + a[u] * b[j]);
res += mn[v];
}
for (auto v : e[u])
for (int i = L[v]; i <= R[v]; i++)
f[u][i] = f[v][i] + res - mn[v];
if (!e[u].size()) f[u][L[u]] = 0;
if (u == 1) cout << res << "\n";
R[u] = cnt;
}
signed main()
{
cin.tie(0)->ios::sync_with_stdio(false);
cin >> n;
for (int i = 2; i <= n; i++) {
int x; cin >> x;
e[x].push_back(i);
}
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) f[i][j] = 1e18;
dfs(1);
return 0;
}
T4
https://www.mxoj.net/problem/P110069?contestId=79
题意
给你 \(n\),将 \(1\sim n\) 按字典序排序,设第 \(i\) 个数为 \(a_i\),求:
\(n < 10^{15}\)。
Solution
首先可以直接暴力字符串 sort。
然后考虑 dfs 出来的就是字典序,然后记一下这个是第几个即可。\(O(n)\)。
后面就鸽了。

浙公网安备 33010602011771号