Skip to content

查看源代码:
MX-S12.md

---
title: 梦熊 NOIP 模拟 4
createTime: 2025/11/23
categories:
    - IT
tags:
    - OI
---

[链接](https://www.luogu.com.cn/contest/287515)

只打了 $2.5\text{h}$。

## A

多对样例瞪眼,你会发现答案只能是 $\max-\min$ 或 $\text{2th-max} - 0$。

这是因为取模只会让数变小。让极差变大的唯一方法是让它成为新的最小值。贪心地让最小值变为 $0$,最大值变为 $\text{2th-max}$。

然后排序,去重。

第一次忘记去重了,然后样例没过。

:::details Code
```cpp
#include <bits/stdc++.h>
using namespace std;

const int maxn=1e5+10;
int a[maxn];

int main() {
//  freopen("mod.in", "r", stdin);
//  freopen("mod.out", "w", stdout);
    
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    int T; cin >> T;
    while (T--) {
        int n; cin >> n;
        for (int i=1; i<=n; i++) {
            cin >> a[i];
        }
        
        sort(a+1, a+n+1);
        int n2 = unique(a+1, a+n+1) - (a+1);
        
        if (n2 == 1) {
            cout << "0\n";
            continue;
        }
        
        cout << max(a[n2]-a[1], a[n2-1]) << '\n';
    }
    
    return 0;
}
```
:::

$\color{green}{100\text{pts}}$

## B

拆分问题的典例。拆完一层还有一层,跟洋葱一样。

$f_{i} \le f_{i+1}$ 的条件略显突兀地摆在这里,实际上它保证了:若 $A \subseteq B$,则 $A$ 一定优于 $B$。

接下来固定左端点,找最前的右端点。将合法条件转化为“设颜色为 $c$ 的最前一个是 $l_c$,最后一个是 $r_c$,若 $i\in[L,R]$ 则 $[l_{c_i}, r_{c_i}]\subseteq[L,R]$”。由于固定了左端点,我们只看右端点的合法性。

不难想到从 $[i,r_{c_i}]$ 开始对内部的每种颜色依次扩展,进而想到 DP 优化,$f(i) = \max\{r_{c_i},\max_{i<j<r_{c_i}} f(j)\}$。

然后我们发现这个区间保证了右端点合法,但是没保证左端点——特判一下即可。

接下来我们得到了很多区间,直接暴力算价值是 $O(n^2)$ 的,但是注意到这些区间不是相离就是包含,因此我们去掉包含其他区间的区间,这样所有区间相离,总长不超过 $n$。

:::details Code
```cpp
#include <bits/stdc++.h>
using namespace std;

const int maxn=1e6+100, inf=0x3f3f3f3f;
vector<int> cc[maxn];
int c[maxn], ci[maxn], cl[maxn], cr[maxn];
int v[maxn], f[maxn];
int rl[maxn], rr[maxn];

struct range {
    int l, r;
};

struct segtreemax {
    int t[maxn*4];
    
    inline int lc(int x) { return x<<1; }
    inline int rc(int x) { return (x<<1)|1; }
    inline int md(int x, int y) { return (x+y)>>1; }
    
    inline void pushup(int tn) {
        t[tn] = max(t[lc(tn)], t[rc(tn)]);
    }
    
    void modify(int tn, int tl, int tr, int i, int a) {
        if (tl == tr) {
            t[tn] = a;
            return;
        }
        
        int mid = md(tl, tr);
        if (i <= mid) {
            modify(lc(tn), tl, mid, i, a);
        } else {
            modify(rc(tn), mid+1, tr, i, a);
        }
        pushup(tn);
    }
    
    int query(int tn, int tl, int tr, int x, int y) {
//      printf("query(#%d[%d,%d], [%d,%d])\n", tn, tl, tr, x, y);
        if (y<tl || tr<x) {
            return -inf;
        }
        
        if (x<=tl && tr<=y) {
            return t[tn];
        }
        
        int mid = md(tl, tr);
        return max(
            query(lc(tn), tl, mid, x, y),
            query(rc(tn), mid+1, tr, x, y)
        );
    }
} sgtr;

struct segtreemin {
    int t[maxn*4];
    
    inline int lc(int x) { return x<<1; }
    inline int rc(int x) { return (x<<1)|1; }
    inline int md(int x, int y) { return (x+y)>>1; }
    
    inline void pushup(int tn) {
        t[tn] = min(t[lc(tn)], t[rc(tn)]);
    }
    
    void modify(int tn, int tl, int tr, int i, int a) {
        if (tl == tr) {
            t[tn] = a;
            return;
        }
        
        int mid = md(tl, tr);
        if (i <= mid) {
            modify(lc(tn), tl, mid, i, a);
        } else {
            modify(rc(tn), mid+1, tr, i, a);
        }
        pushup(tn);
    }
    
    int query(int tn, int tl, int tr, int x, int y) {
        if (y<tl || tr<x) {
            return inf;
        }
        
        if (x<=tl && tr<=y) {
            return t[tn];
        }
        
        int mid = md(tl, tr);
        return min(
            query(lc(tn), tl, mid, x, y),
            query(rc(tn), mid+1, tr, x, y)
        );
    }
} sgtl;

int main() {
//  freopen("interval.in", "r", stdin);
//  freopen("interval.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    int n; cin >> n;
    for (int i=1; i<=n; i++) {
        cin >> c[i];
        cc[c[i]].push_back(i);
        ci[i] = cc[c[i]].size();
        cr[c[i]] = i;
        cl[c[i]] = cl[c[i]]? cl[c[i]]: i;
    }
    for (int i=1; i<=n; i++) {
        cin >> v[i];
    }
    for (int i=1; i<=n; i++) {
        cin >> f[i];
    }
    
    for (int i=n; i>=1; i--) {
        rr[i] = max(cr[c[i]], sgtr.query(1, 1, n, i+1, cr[c[i]]-1));
        sgtr.modify(1, 1, n, i, rr[i]);
//      printf("rr[%d] <- %d\n", i, rr[i]);
    }
    for (int i=1; i<=n; i++) {
        sgtl.modify(1, 1, n, i, cl[c[i]]);
    }
    
    stack<range> s;
    for (int i=1; i<=n; i++) {
        if (sgtl.query(1, 1, n, i, rr[i]) < i) {
            continue;
        }
        
//      printf("ok: [%d, %d]\n", i, rr[i]);
        
        while (!s.empty() && s.top().r>=rr[i]) {
            s.pop();
        }
        
        s.push({i, rr[i]});
    }
    
    long long ans = inf;
    while (!s.empty()) {
        range rg = s.top(); s.pop();
        long long val = 0;
        for (int i=rg.l; i<=rg.r; i++) {
            val += v[i] * f[i-rg.l+1];
        }
        ans = min(ans, val);
    }
    
    cout << ans;
    
    return 0;
}
```
:::

$\color{green}{100\text{pts}}$

## C, D

C 暴力,dfs + 剪枝。

D 可以看出来是从低位到高位贪心合并,考场上没证。

:::details C
```cpp
#include <bits/stdc++.h>
using namespace std;

const int maxn=5010, mod=998244353, inf=0x3f3f3f3f;
int n;
int op[maxn];
int p[maxn];
int pmin[maxn], pmax[maxn];
bool vis[maxn];
int ans;

bool check(int i) {
//  printf("check ");
//  for (int j=1; j<=i; j++) {
//      printf("%d ", p[j]);
//  }
    
    bool ok = true;
    
    if (op[i] == 0) {
        ok = ok && p[i] < pmin[i-1];
    } else if (op[i] == 1) {
        ok = ok && p[i] > pmax[i-1];
    }
    
    for (int j=1; ok&&j<i; j++) {
        if (op[j] == 2) {
            ok = ok && p[j] < p[i];
        } else if (op[j] == 3) {
            ok = ok && p[j] > p[i];
        }
    }
    
//  printf(": %d\n", ok);
    
    return ok;
}

void dfs(int i) {
    if (i > n) {
        ans++;
        ans %= mod;
        return;
    }
    
    for (p[i]=1; p[i]<=n; p[i]++) {
        if (!vis[p[i]] && check(i)) {
            vis[p[i]] = true;
            pmin[i] = min(pmin[i-1], p[i]);
            pmax[i] = max(pmax[i-1], p[i]);
            
            dfs(i+1);
            
            vis[p[i]] = false;
        }
    }
}

int main() {
    //  freopen("*.in", "r", stdin);
    //  freopen("*.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    cin >> n;
    for (int i=1; i<=n; i++) {
        cin >> op[i];
    }
    
    pmin[0] = inf;
    pmax[0] = -inf;
    dfs(1);
    
    cout << ans;
    
    return 0;
}
```
:::

:::details D
```cpp
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn=1e6+10, mod=998244353;
int b, nl, nr;
int l[maxn], r[maxn];

bool ller() {
    // 留多一位防止溢出
    for (int i=nr; i>=0; i--) {
        if (l[i] < r[i]) {
            return true;
        }
        if (l[i] > r[i]) {
            return false;
        }
    }
    return true;
}

void linc() {    
    int carry = 1;
    
    // 留多一位防止溢出
    for (int i=0; i<=nr; i++) {
        l[i] += carry;
        carry = (l[i] >= b);
        if (l[i] >= b) { l[i] -= b; }
    }
}

int ans[maxn];
ll solve() {
    memset(ans, 0, sizeof(ans));
    
//  printf("solve: ");
    int cur = 0;
    ans[0] = 0;
    
    for (int i=0; i<=nr-1; i++) {
        if (ans[cur] + l[i] < b) {
            ans[cur] += l[i];
        } else {
            cur++;
            ans[cur] = l[i];
        }
//      printf("%d ", l[i]);
    }
//  printf("; cur=%d; ", cur);
    
    ll a=0;
    for (int i=cur; i>=0; i--) {
        a *= b;
        a %= mod;
        a += ans[i];
        a %= mod;
    }
    
//  printf("ans: %d\n", a);
    return a;
}

signed main() {
    //  freopen("*.in", "r", stdin);
    //  freopen("*.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    int T; cin >> T;
    while (T--) {
        memset(l, 0, sizeof(l));
        memset(r, 0, sizeof(r));
        
        cin >> b >> nl;
        for (int i=0; i<=nl-1; i++) {
            cin >> l[i];
        }
        cin >> nr;
        for (int i=0; i<=nr-1; i++) {
            cin >> r[i];
        }
        
        ll ans=0, cnt=0;
        for (; ller(); linc()) {
//          printf("{\n");
            ans += solve();
            ans %= mod;
//          printf("}\n");

//          cnt++;
//          if (cnt % 1000 == 0) {
//              printf("%d-th loop\n", cnt);
//          }
        }
        
        cout << ans << '\n';
    }
    
    return 0;
}
```
:::

$\color{red}{15+16}$,虽然看起来小但是加起来也不错了。