退役(JSOI2026)前一周做题记录

20260224

Amereistr

显然的,我们如果知道 \(mask\) 这些钥匙都收集的最小时间,子集取 \(\min\) DP 一下即可。

发现从操作走到操作不太好搞,我们把操作挂在与“距离”关系更大的图的节点上。

对于每个节点,所有操作按照时间排序。

然后我们有状态 \((u,mask)\) 表示在 \(u\),当前收集了 \(mask\)

我们有两种转移,同 \(mask\) 与最短路一样,由于是同一层的我们一起做掉;然后对于该层,我们可以在若干点收集钥匙并往上走,即,枚举每一个点上挂的操作,按照时间倒序枚举,确保操作的时间都在 \((u,mask)\) 之前;层间转移即可。

复杂度 \(O(n2^k\log n+m2^k)\)

然而我赛时写了一个奇异搞笑做法。大样例跑了 7s,却赛后通过。不想补了,扔个假的在这吧。

// Note: Fake
#include <bits/stdc++.h>
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using i128 = __int128;
void File(string s) { freopen((s + ".in").c_str(), "r", stdin), freopen((s + ".out").c_str(), "w", stdout); }
template<typename T>
inline void chkmax(T &a, const T b) { if (a < b) a = b; }
template<typename T>
inline void chkmin(T &a, const T b) { if (a > b) a = b; }
inline void iofast() { cin.tie(0), cout.tie(0), ios::sync_with_stdio(0); }
const int inf = 0x3f3f3f3f, P = 998244353;
const int M = 20005;
vector<pair<int, int> > g[M];
struct Layer {
    ll dis[M];
    ll T;
    priority_queue<pair<ll, int>, vector<pair<ll, int> >, greater<pair<ll, int> > > pq;
    void init() {
        memset(dis, 0x3f, sizeof(dis));
        T = 0x3f3f3f3f3f3f3f3f;
    }
    Layer () {
        init();
    }
    void upd(int p, ll t) {
        if (dis[p] <= t) return ;
        chkmin(T, t);
        dis[p] = t;
        pq.push({ t, p });
        while (!pq.empty()) {
            auto [d, u] = pq.top();
            pq.pop();
            if (d > dis[u]) continue;
            for (auto [v, w] : g[u]) {
                if (dis[v] > dis[u] + w) {
                    pq.push({ dis[v] = dis[u] + w, v });
                }
            }
        }
    }
} lay[1 << 10];
int n, m;
ll dp[1 << 10];
struct JMT {
    int x, y, z;
};
void solve() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) g[i].clear();
    for (int i = 1, a, b, w; i <= m; i++) {
        scanf("%d%d%d", &a, &b, &w);
        g[a].push_back({ b, w });
        g[b].push_back({ a, w });
    }
    int k, q;
    scanf("%d%d", &k, &q);
    for (int i = 0; i < (1 << k); i++) lay[i].init();
    lay[0].upd(1, 0);
    vector<JMT> vec;
    for (int i = 0; i < q; i++) {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        vec.push_back({ x, y, --z });
    }
    sort(vec.begin(), vec.end(), [&](JMT a, JMT b) { return a.x < b.x; });
    for (int i = 0; i < q; i++) {
        auto [x, y, z] = vec[i];
        for (int j = 0; j < (1 << k); j++) if (!(j >> z & 1) && lay[j].dis[y] <= x) {
            lay[j | (1 << z)].upd(y, x);
        }
    }
    memset(dp, 0x3f, sizeof(dp));
    dp[0] = 0;
    for (int i = 1; i <= k; i++) {
        for (int j = (1 << k) - 1; j; j--) {
            for (int l = j; ; l = (l - 1) & j) {
                chkmin(dp[j], max(dp[j ^ l], lay[l].T));
                if (!l) break;
            }
        }
        if (dp[(1 << k) - 1] != 0x3f3f3f3f3f3f3f3f) printf("%lld ", dp[(1 << k) - 1]);
        else printf("-1 ");
    }
    puts("");
}
signed main() {
    File("amereistr");
    int t;
    scanf("%d", &t);
    while (t--) solve();
    return 0;
}

World's end BLACKBOX

考虑如何计算 \(A\) 的权值,显然就是对于所有数 \(x\) 满足 \(\sum_{v\in A,v\le x}v=x-1\)\(\min x\) 为答案。

考虑观察它的性质:对于 \(\le x\) 的数组成的集合 \(S\),分为最大的数 \(v\) 和其余所有数,其余所有数的和必然 \(\ge v-1\),而不在 \(S\) 内最小的数必然 \(>(v-1)+v+1=2v\),即下一个数至少是 \(v\) 的两倍。

看到有一个“超过两倍”的关系,考虑以 \(2\) 为底倍增值域分块,如果 \(v\) 在一个块 \([2^{i-1},2^i)\) 中,则要求比 \(v\) 大的数中最小应 \(\ge 2^i\)

考虑先枚举当前层(即 \(i\))然后枚举右端点,显然对于一个合法的左端点,要求存在 \([2^{i-1},2^i)\) 内的数,同时对于 \(\sum_{v<2^i}v<\min_{v\ge 2^i}v\)。第一个限制说明左端点有右界,第二个限制说明左端点有左界。因此可以尺取。

鉴于在定义中是“最小的 \(x\)”,所以当前的左端点区间必须不能包含上一层中,对于该右端点,左端点的区间(已经有更小的 \(x\) 了)。记录一下上次的区间即可。注意到,随着 \(i\) 的增加,对于固定右端点,左端点的合法右界是左移的,因此右界就是上一次的左界 \(-1\)(一个简单的原因,就是区间越长答案显然不减,因此如果答案变大了区间一定边长)。

快速计算区间答案可以用前缀和、二次前缀和,较为容易。

需要注意的是,前缀和不能取模,因为设计比较;而二次前缀和需要取模,否则会爆 long long

#include <bits/stdc++.h>
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using i128 = __int128;
void File(string s) { freopen((s + ".in").c_str(), "r", stdin), freopen((s + ".out").c_str(), "w", stdout); }
template<typename T>
inline void chkmax(T &a, const T b) { if (a < b) a = b; }
template<typename T>
inline void chkmin(T &a, const T b) { if (a > b) a = b; }
inline void iofast() { cin.tie(0), cout.tie(0), ios::sync_with_stdio(0); }
const int inf = 0x3f3f3f3f, P = 998244353;
const int M = 1000006;
int n;
int a[M], R[M];
ll w[M];
ll s[M], ss[M];
int que[M];
signed main() {
    File("blackbox");
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", a + i), R[i] = i;
    ll ans = 0;
    for (int i = 1; i <= 30; i++) {
        for (int j = 1; j <= n; j++) {
            s[j] = s[j - 1];
            if (a[j] >= (1 << i)) w[j] = a[j];
            else w[j] = 1e18, s[j] += a[j];
            ss[j] = ss[j - 1] + s[j] % P;
        }
        int L = 1, ql = 1, qr = 0;
        for (int j = 1; j <= n; j++) {
            while (ql <= qr && w[j] <= w[que[qr]]) qr--;
            que[++qr] = j;
            while (L <= j && w[que[ql]] <= s[j] - s[L - 1] + 1) {
                L++;
                while (ql <= qr && que[ql] < L) ql++;
            }
            if (R[j] >= L) {
                ans += (s[j] + 1) % P * (R[j] - L + 1) % P;
                ans += P - (ss[R[j] - 1] - ss[L - 2]) % P;
                R[j] = L - 1;
            }
        }
    }
    printf("%lld\n", ans % P);
    return 0;
}

20260224

彩灯覆盖

考虑按题意建树,变成任意祖先链最多选 \(k\) 个,问最多权值和。

对于每一颗树单独考虑。

如果采用我最开始想到的 DP:\(dp_{u,k}\) 表示 \(u\) 这个点子树满足 \(k\) 的条件的时候,最大权值。此时的两个更新中,第二种 \(dp_{u,k}\gets \sum_{(u,v)}dp_{v,k-1}+a_u\) 是难以做到的。

考虑一种贪心的决策:对于节点 \(u\),如果在第一个子树内选择了 \(k\) 层,则其余均可选 \(k\) 层。那么对于链的情况,选择 \(k\) 层可以维护一个有序数列,从大到小排序,\(k\) 的答案即为前 \(k\) 项的和。扩展到这里,如果把两个子树合并,即把这两个子树的数列前若干项依次相加,即,如果在 \(v_1\) 子树内选择了第 \(k\) 小的点,则 \(v_2\) 也一定可以选一个第 \(k\) 小的点。

于是类启发式合并即可完成;注意到该数列长度为 \(O(dep)\) 级别的,长剖一下就可以了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void File(string s) {
    freopen((s + ".in").c_str(), "r", stdin);
    freopen((s + ".out").c_str(), "w", stdout);
}
const int M = 300005;
int n, m;
struct Itv {
    int l, r, v;
    friend bool operator < (Itv a, Itv b) {
        return a.l != b.l ? a.l < b.l : a.r > b.r;
    }
} a[M];
bool cov(Itv a, Itv b) {
    return a.l <= b.l && a.r >= b.r;
}
void chkmax(int &A, int B) { if (A < B) A = B; }
void chkmin(int &A, int B) { if (A > B) A = B; }
int stk[M], tp;
int fa[M];
vector<int> g[M];
int val[M];
int dep[M];
int depth[M];
int son[M];
void dfs(int u) {
    dep[u] = dep[fa[u]] + 1;
    for (auto v : g[u]) {
        dfs(v);
        if (depth[v] > depth[u]) {
            depth[u] = depth[v];
            son[u] = v;
        }
    }
    depth[u]++;
}
ll sum;
void dfs3(int u, priority_queue<ll> &pq) {
    if (son[u]) dfs3(son[u], pq);
    for (auto v : g[u]) if (v != son[u]) {
        priority_queue<ll> p;
        dfs3(v, p);
        vector<ll> ad;
        while (!p.empty()) {
            ll x = p.top() + pq.top();
            p.pop(), pq.pop();
            ad.push_back(x);
        }
        for (auto v : ad) pq.push(v);
    }
    pq.push(val[u]);
}
ll ans1[M], ans2[M];
void work(int rt) {
    dfs(rt);
    priority_queue<ll> pq;
    dfs3(rt, pq);
    ll s = 0;
    int sz = pq.size();
    for (int i = 1; !pq.empty(); i++) {
        ll v = pq.top();
        pq.pop();
        ans1[i] += (s += v);
    }
    ans2[sz + 1] += s;
}

signed main() {
    File("cover");
    scanf("%d%d", &n, &m);
    n = m;
    int tot = 0;
    for (int i = 1; i <= m; i++) {
        tot++;
        scanf("%d%d%d", &a[tot].l, &a[tot].r, &a[tot].v);
        a[tot].r--;
    }
    m = tot;
    sort(a + 1, a + m + 1);
    for (int i = 1; i <= m; i++) {
        while (tp && !cov(a[stk[tp]], a[i])) {
            tp--;
            if (tp == 0) work(stk[tp + 1]);
        }
        if (tp) g[stk[tp]].push_back(i), fa[i] = stk[tp];
        stk[++tp] = i;
        val[i] = a[i].v;
    }
    work(stk[1]);
    for (int i = 1; i <= n; i++) ans2[i] += ans2[i - 1];
    for (int i = 1; i <= n; i++) printf("%lld ", ans1[i] + ans2[i] + sum);
    return 0;
}

计数题

考虑合并的实质可以看做删除:删除相邻的 01 或者对于连续三个相同的数删掉两个。

因此一定保留的是原串的子序列。考虑我们作一个子序列自动机——不过这里的子序列指的是,中间跳过的部分都能被删干净的子序列,即答案序列。

正确性问题,即,如果有 \(i\to j_1,i\to j_2,j_1<j_2,a_{j_1}=a_{j_2}\)\(j_1,j_2\) 奇偶性相同。要求如果 \(i\to j_1\) 则须有 \(i\to j_2\),此时我们将 \([j_1,j_2-1]\) 合并为一个数 \(c\),然后把 \(s_{j_1},c,s_{j_2}\) 合并即可,都可以起到删除 \(s_{j-1},c\) 的效果。

考虑怎么构建自动机:

显然 \(i\) 可以连向 \(i+1\),如果 \(a_i=a_{i+1}\),那么只需要找下一个与 \(i\) 奇偶性不同的 \(j\) 使得 \(a_j\neq a_i\) 即可。

如果 \(a_i\neq a_{i+1}\),则后面跟奇数个不一样的(先缩为 1 个),再跟 2 个一样的即可。因此需要找到第一个奇偶性与 \(i\) 不同的 \(j\),满足 \(a_{j-1}=a_j=a_i\)

一个需要注意的点,判断一个位置 \(i\) 是否能直接作为结尾,首先是奇偶性,其次是它的第二种情况,即后面存在不同奇偶性的 \(j\) 满足 \(a_i=a_{j-1}=a_j\),或者是 \(a_i=a_n\)。这个需要手写判断。

#include <bits/stdc++.h>
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using i128 = __int128;
void File(string s) { freopen((s + ".in").c_str(), "r", stdin), freopen((s + ".out").c_str(), "w", stdout); }
template<typename T>
inline void chkmax(T &a, const T b) { if (a < b) a = b; }
template<typename T>
inline void chkmin(T &a, const T b) { if (a > b) a = b; }
inline void iofast() { cin.tie(0), cout.tie(0), ios::sync_with_stdio(0); }
const int inf = 0x3f3f3f3f, P = 998244353;
const int M = 5000006;
int n;
char s[M];
int a[M];
int rec[2][M];
int two[2][M];
int calc_same(int p, int c) {
    return two[c][p + 1];
}
int to[M][2];
int dp[M];
void madd(int &a, int b) {
    if ((a += b) >= P) a -= P;
}
void solve() {
    scanf("%s", s + 1);
    n = strlen(s + 1);
    for (int i = 1; i <= n; i++) a[i] = s[i] - '0';
    rec[0][n + 2] = rec[1][n + 2] = n + 2;
    rec[0][n + 1] = rec[1][n + 1] = n + 1;
    for (int i = n; i; i--) {
        for (int j : { 0, 1 }) rec[j][i] = rec[j][i + 2];
        rec[a[i]][i] = i;
    }
    memset(dp, 0, sizeof(dp));
    dp[1] = 1;
    for (int i = 3; i <= n; i += 2) if (a[i] != a[1] && a[i] == a[i - 1]) { dp[i] = 1; break; }
    two[0][n + 2] = two[1][n + 2] = n + 2;
    two[0][n + 1] = two[1][n + 1] = n + 1;
    for (int i = n; i; i--) {
        for (int j : { 0, 1 }) two[j][i] = two[j][i + 2];
        if (i > 1 && a[i] == a[i - 1]) two[a[i]][i] = i;
    }
    for (int i = 1; i <= n; i++) to[i][a[i + 1]] = i + 1;
    for (int i = 1; i < n; i++) {
        if (a[i] == a[i + 1]) to[i][!a[i + 1]] = rec[!a[i + 1]][i + 1];
        else to[i][!a[i + 1]] = calc_same(i, !a[i + 1]);
    }
    for (int i = 1; i <= n; i++) {
        if (to[i][0] <= n) madd(dp[to[i][0]], dp[i]);
        if (to[i][1] <= n) madd(dp[to[i][1]], dp[i]);
        if (i && !(n - i & 1) && (a[i] == a[n] || two[a[i]][i + 1] <= n)) madd(dp[n + 1], dp[i]);
    }
    printf("%d\n", dp[n + 1]);
}
signed main() {
    File("count");
    int t;
    scanf("%d", &t);
    while (t--) solve();
    return 0;
}

20260225

激光炮

首先,把让 \(n\ge m\),使得方便对列状压 DP;每一列三个状态:全都被覆盖,有空缺,和有激光炮。考虑枚举一行的状态转移,发现复杂度太高;我们发现我们只关心一段内是否存在行间激光炮,而不关心其具体位置,因此可以一列一列的枚举,再记录一个行状态:都覆盖,有空缺和有激光炮。转移分讨即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void File(string s) { freopen((s + ".in").c_str(), "r", stdin); freopen((s + ".out").c_str(), "w", stdout); }
const int N = 12, M = 155, inf = 0x3f3f3f3f;
int dp[3][540000], F[3][540000];
void chkmin(int &a, int b) { if (a > b) a = b; }
int pw[N + 2];
inline int kbit(int v, int k) {
    return (v / pw[k]) % 3;
}
int n, m;
char tmps[M][M], s[M][N];
void solve() {
    memset(dp, 0x3f, sizeof(dp));
    dp[0][0] = 0;
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++) scanf("%s", tmps[i]);
    if (n < m) {
        for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) s[j][i] = tmps[i][j];
        swap(n, m);
    }
    else for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) s[i][j] = tmps[i][j];
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            memset(F, 0x3f, sizeof(F));
            if (s[i][j] != '#') {
                for (int cur : { 0, 1, 2 }) {
                    for (int msk = 0; msk < pw[m]; msk++) {
                        // don't put
                        if (kbit(msk, j) == 2) {
                            // already exist, 2
                            chkmin(F[cur][msk], dp[cur][msk]);
                        }
                        else if (cur) {
                            // line existed / will exist, don't change
                            chkmin(F[cur][msk], dp[cur][msk]);
                        }
                        else {
                            // need
                            // msk = 1, cur = 0
                            chkmin(F[0][msk - kbit(msk, j) * pw[j] + pw[j]], dp[cur][msk]);
                            // cur = 1
                            chkmin(F[1][msk], dp[cur][msk]);
                        }
                        // put, 2, cur = 2
                        chkmin(F[2][msk - kbit(msk, j) * pw[j] + 2 * pw[j]], dp[cur][msk] + 1);
                    }
                }
            }
            if (s[i][j] != '.') {
                for (int cur : { 0, 1, 2 }) {
                    for (int msk = 0; msk < pw[m]; msk++) {
                        if (kbit(msk, j) != 1 && cur != 1) {
                            chkmin(F[0][msk - kbit(msk, j) * pw[j]], dp[cur][msk]);
                        }
                    }
                }
            }
            memcpy(dp, F, sizeof(dp));
        }
        for (int cur : { 0, 1, 2 }) {
            for (int msk = 0; msk < pw[m]; msk++) {
                if (cur == 1) dp[cur][msk] = inf;
                if (cur == 2) chkmin(dp[0][msk], dp[cur][msk]), dp[cur][msk] = inf;
            }
        }
    }
    int ans = inf;
    for (int msk = 0; msk < pw[m]; msk++) {
        bool f = false;
        for (int j = 0; j < m; j++) if (kbit(msk, j) == 1) { f = true; break; }
        if (!f) chkmin(ans, dp[0][msk]);
    }
    printf("%d\n", ans);
}
signed main() {
    File("laser");
    pw[0] = 1;
    for (int i = 1; i <= N; i++) pw[i] = pw[i - 1] * 3;
    int c, t;
    scanf("%d%d", &c, &t);
    while (t--) solve();
    return 0;
}

最长单调子串问题

我们直接猜结论:一个数只可能被改成 \(-\inf\) or \(\inf\)

此时我们对于六种状态:当前升 or 降,最后一位 \(-\inf\) or \(a_i\) or \(\inf\),贪心的保留操作次数最小的即可,想通则末尾连续段最短。

#include <bits/stdc++.h>
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using i128 = __int128;
void File(string s) { freopen((s + ".in").c_str(), "r", stdin), freopen((s + ".out").c_str(), "w", stdout); }
template<typename T>
inline void chkmax(T &a, const T b) { if (a < b) a = b; }
template<typename T>
inline void chkmin(T &a, const T b) { if (a > b) a = b; }
inline void iofast() { cin.tie(0), cout.tie(0), ios::sync_with_stdio(0); }
const int inf = 0x3f3f3f3f, P = 998244353;
const int M = 400005;
int n, m;
struct Node {
    int opt, l1, l2, k;
    Node (int o = inf, int l = inf, int L = inf, int K = 0) { opt = o, l1 = l, l2 = L, k = K; } 
    Node upd(int v1, int v2) {
        Node res = *this;
        if ((v2 - v1) * k >= 0) {
            res.l1++;
            if (v1 == v2) res.l2++; else res.l2 = 1;
            if (abs(v2) >= inf) res.opt++;
        }
        else {
            res.k = -res.k;
            res.l1 = res.l2 + 1;
            res.l2 = 1;
            if (abs(v2) >= inf) res.opt++;
        }
        return res;
    }
    void chk(Node a) {
        if (a.l1 >= m) return ;
        if (a.opt < opt) *this = a;
        else if (a.opt == opt && a.l1 < l1) *this = a;
        else if (a.opt == opt && a.l1 == l1 && a.l2 < l2) *this = a;
    }
};
Node dp[M][2][3];
int a[M];
void solve() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", a + i);
    dp[1][0][0] = { 1, 1, 1, -1 };
    dp[1][0][1] = { 0, 1, 1, -1 };
    dp[1][0][2] = { 1, 1, 1, -1 };
    dp[1][1][0] = { 1, 1, 1, 1 };
    dp[1][1][1] = { 0, 1, 1, 1 };
    dp[1][1][2] = { 1, 1, 1, 1 };
    for (int i = 2; i <= n; i++) {
        for (auto o : { 0, 1 }) {
            for (auto O : { 0, 1, 2 }) {
                dp[i][o][O] = {};
            }
        }
        dp[i][0][0].chk(dp[i - 1][0][0].upd(-inf, -inf - 1));
        dp[i][0][0].chk(dp[i - 1][1][0].upd(-inf, -inf - 1));
        dp[i][1][0].chk(dp[i - 1][0][0].upd(-inf - 1, -inf));
        dp[i][1][0].chk(dp[i - 1][1][0].upd(-inf - 1, -inf));
        dp[i][0][0].chk(dp[i - 1][0][1].upd(a[i - 1], -inf));
        dp[i][0][0].chk(dp[i - 1][1][1].upd(a[i - 1], -inf));
        dp[i][0][0].chk(dp[i - 1][0][2].upd(inf, -inf));
        dp[i][0][0].chk(dp[i - 1][1][2].upd(inf, -inf));
        
        dp[i][1][2].chk(dp[i - 1][0][0].upd(-inf, inf));
        dp[i][1][2].chk(dp[i - 1][1][0].upd(-inf, inf));
        dp[i][1][2].chk(dp[i - 1][0][1].upd(a[i - 1], inf));
        dp[i][1][2].chk(dp[i - 1][1][1].upd(a[i - 1], inf));
        dp[i][1][2].chk(dp[i - 1][0][2].upd(inf, inf + 1));
        dp[i][1][2].chk(dp[i - 1][1][2].upd(inf, inf + 1));
        dp[i][0][2].chk(dp[i - 1][0][2].upd(inf + 1, inf));
        dp[i][0][2].chk(dp[i - 1][1][2].upd(inf + 1, inf));

        dp[i][1][1].chk(dp[i - 1][0][0].upd(-inf, a[i]));
        dp[i][1][1].chk(dp[i - 1][1][0].upd(-inf, a[i]));
        dp[i][0][1].chk(dp[i - 1][0][2].upd(inf, a[i]));
        dp[i][0][1].chk(dp[i - 1][1][2].upd(inf, a[i]));
        if (a[i] > a[i - 1])
            dp[i][1][1].chk(dp[i - 1][0][1].upd(a[i - 1], a[i])), dp[i][1][1].chk(dp[i - 1][1][1].upd(a[i - 1], a[i]));
        else if (a[i] < a[i - 1])
            dp[i][0][1].chk(dp[i - 1][0][1].upd(a[i - 1], a[i])), dp[i][0][1].chk(dp[i - 1][1][1].upd(a[i - 1], a[i]));
        else
            dp[i][0][1].chk(dp[i - 1][0][1].upd(a[i - 1], a[i])), dp[i][1][1].chk(dp[i - 1][1][1].upd(a[i - 1], a[i]));
    }
    printf("%d\n", min({ dp[n][0][0].opt, dp[n][0][1].opt, dp[n][0][2].opt, dp[n][1][0].opt, dp[n][1][1].opt, dp[n][1][2].opt }));
}
signed main() {
    File("lis");
    int c, t;
    scanf("%d%d", &c, &t);
    while (t--) solve();
    return 0;
}
posted @ 2026-02-26 00:31  Air_CoIor5  阅读(0)  评论(0)    收藏  举报