• 四月 杂题题解


    最小步数

    题意

    \(n+1\)\(m\) 列的表格,从最第一行任意点开始,每次向右或向下走一格

    对于 \(1\le i\le n\) 有区间 \([A_i,B_i]\) 在这些各自上时,不能向下移动。

    对于 \(2\le K\le H+1\) ,求出从第一行到第 \(K\) 行的最少步数

    sol

    线段树,若对于区间 \([L,R]\) 不能向下走,则

    • 区间 \([1,L-1],[R+1,m]\) 全部加 1

    • \(i\in[L,R]\) 改为 \(dis(L-1)+i-L+1\) 因为必须从 \(L-1\) 走来

      具体的:记录当前区间左端点的值,以及是否需要下传,每次懒标记就更新这个值

    • 求最小值

    两个修改一个查询

    code

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e5 + 5;
    const int INF = 1e8;
    int n, m, a[N], b[N], R, st, fl[N << 2], ad[N << 2];
    int mn[N << 2], le[N << 2], ans[N];
    struct Seg {
        int l, r;
    } T[N << 2];
    #define ls (rt << 1)
    #define rs (rt << 1 | 1)
    inline void Up(int rt) {
        mn[rt] = min(mn[ls], mn[rs]);
        le[rt] = le[ls];
    }
    inline void down(int rt) {
        if (fl[rt]) {
            register int L = le[rt];
            fl[ls] = 1, ad[ls] = 0, mn[ls] = le[ls] = L;
            fl[rs] = 1, ad[rs] = 0, mn[rs] = le[rs] = L + T[rs].l - T[rt].l;
        } else if (ad[rt]) {
            ad[ls] += ad[rt], mn[ls] += ad[rt], le[ls] += ad[rt];
            ad[rs] += ad[rt], mn[rs] += ad[rt], le[rs] += ad[rt];
        }
        ad[rt] = fl[rt] = 0;
    }
    void bui(int l, int r, int rt) {
        T[rt].l = l, T[rt].r = r;
        if (l == r)
            return;
        register int mid = l + r >> 1;
        bui(l, mid, ls), bui(mid + 1, r, rs);
    }
    void add(int ql, int qr, int rt) {
        if (ql <= T[rt].l && T[rt].r <= qr) {
            ++mn[rt], ++ad[rt], ++le[rt];
            return;
        }
        down(rt);
        if (ql <= T[ls].r)
            add(ql, qr, ls);
        if (qr >= T[rs].l)
            add(ql, qr, rs);
        Up(rt);
    }
    void upd(int ql, int qr, int L, int rt) {
        if (ql <= T[rt].l && T[rt].r <= qr) {
            le[rt] = mn[rt] = L + T[rt].l - ql + 1;
            fl[rt] = 1;
            return;
        }
        down(rt);
        if (ql <= T[ls].r)
            upd(ql, qr, L, ls);
        if (qr >= T[rs].l)
            upd(ql, qr, L, rs);
        Up(rt);
    }
    int ask(int p, int rt) {
        if (T[rt].l == T[rt].r)
            return mn[rt];
        down(rt);
        return p <= T[ls].r ? ask(p, ls) : ask(p, rs);
    }
    #undef ls
    #undef rs
    int main() {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++) scanf("%d%d", &a[i], &b[i]);
        bui(1, m, 1);
        for (int i = 1; i <= n; i++) ans[i] = INF;
        for (int i = 1, L; i <= n; i++) {
            if (a[i] > 1)
                add(1, a[i] - 1, 1);
            if (b[i] < m)
                add(b[i] + 1, m, 1);
            if (a[i] > 1)
                L = ask(a[i] - 1, 1);
            else
                L = INF;
            upd(a[i], b[i], L, 1);
            ans[i] = min(ans[i], mn[1]);
            if (ans[i] == INF)
                break;
        }
        for (int i = 1; i <= n; i++) printf("%d\n", ans[i] < INF ? ans[i] : -1);
    }
    

    彩色蜡笔

    题意

    \(n\) 只由 \(R_i,G_i,B_i\) 表示的彩色蜡笔,

    \(i,j\) 两只蜡笔的色彩差异度为 \(\max(|R_i-R_j|,|G_i-G_j|,|B_i-B_j|)\)

    选出 \(K\) 只蜡笔组成的子序列(不用连续),使差异度最小

    \(R_i,G_i,B_i\le255,n\le 10^5\)

    sol

    三维前缀和,值域小,可将问题转为判断性问题。

    求三维空间内的点是否 \(\ge K\)

    • 二分答案,枚举 \(R,G,B\) 上界,\(O(1)\) 判断,总 \(O(255^3\log 255)\)
    • 对一维,如 \(R\) 使用双指针,枚举 \(G,B\) 上界,总 \(O(255^3)\)

    code

    #include <bits/stdc++.h>
    using namespace std;
    typedef unsigned long long uLL;
    typedef long double LD;
    typedef long long LL;
    typedef double db;
    const int M = 260;
    int n, K, s[M][M][M], ans = 1e9, L, R;
    inline bool chk() {
        register int x = R - L + 1;
        for (int i = x; i <= 256; i++)
            for (int j = x; j <= 256; j++)
                if (s[R][i][j] - s[L - 1][i][j] - s[R][i - x][j] - s[R][i][j - x] + s[L - 1][i - x][j] +
                        s[L - 1][i][j - x] + s[R][i - x][j - x] - s[L - 1][i - x][j - x] >=
                    K)
                    return 1;
        return 0;
    }
    int main() {
        scanf("%d%d", &n, &K);
        for (int i = 1, x, y, z; i <= n; i++) scanf("%d%d%d", &x, &y, &z), s[x + 1][y + 1][z + 1]++;
        for (int i = 1; i <= 256; i++)
            for (int j = 1; j <= 256; j++)
                for (int k = 1; k <= 256; k++)
                    s[i][j][k] += s[i - 1][j][k] + s[i][j - 1][k] + s[i][j][k - 1] - s[i - 1][j - 1][k] -
                                  s[i - 1][j][k - 1] - s[i][j - 1][k - 1] + s[i - 1][j - 1][k - 1];
        for (L = 1, R = 1; R <= 256; R = max(R, ++L)) {
            while (R <= 256 && !chk()) R++;
            if (R <= 256)
                ans = min(ans, R - L);
        }
        printf("%d", ans);
    }
    

    图的数量

    题意

    \(n\) 个点, \(m\) 条未标号边,且满足下列条件:

    • 没有自环
    • 每个点度最大为 2
    • 最大连通块刚好 \(L\) 个点

    求图的数量, \(\mod 10^9+7\)

    sol

    1. 度不超过 2,即不是环就是链

    2. 最大连通块数量恰好为 \(K\) ,不好求,考虑前缀和做差

      \(F_k\) 为最大连通块大小不超过 \(k\) 的数量,则答案为 \(F_k-F_{k-1}\)

    \(f_{i,j}\) 为用了 \(i\) 个点, \(j\) 条边的满足条件的图的数量

    • \(f_{i,j}\rightarrow f_{i+1,j}\) ,将 \(i\) 单独做连通块

    • 若点 \(i\) 在一条长度为 \(k(2\le k\le L)\) 的链上,

      \(f_{i,j}\times C_{n-i-1}^{k-1}\times \dfrac{k!}{2}\rightarrow f_{i+k,j+k-1}\)

      即该点必选,再从 \(n-i-1\) 中选 \(k-1\) 个点,选出的点组成 \(\dfrac{k!}{2}\) 条链

    • \(i\) 在长度为 2 的链上,则 \(f_{i,j}\times(n-i-1)\rightarrow f_{i+2,j+2}\)

    • 该点在长度为 \(k(3\le k\le L)\) ,则

      \(f_{i,j}\cdot C_{n-i-1}^{k-1}\cdot\frac{(k-1)!}{2}\rightarrow f_{i+k,j+k}\)

    即可求出 \(F_k\) ,同理求出 \(F_{k-1}\)

    记得逆元

    code

    #include <bits/stdc++.h>
    using namespace std;
    typedef unsigned long long uLL;
    typedef long double LD;
    typedef long long LL;
    typedef double db;
    const LL P = 1e9 + 7;
    const LL inv2 = 500000004;
    int n, m, MX;
    LL C[1005][1005], f[305][305], fac[1005], fR, fL;
    inline int get(int L) {
        if (L == 1)
            return 0;
        memset(f, 0, sizeof(f));
        f[0][0] = 1;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j <= m; j++) {
                (f[i + 1][j] += f[i][j]) %= P;
                if (i + 2 <= n && j + 2 <= m)
                    (f[i + 2][j + 2] += f[i][j] * (n - i - 1) % P) %= P;
                for (int k = 2; k <= L && i + k <= n; k++) {
                    if (j + k - 1 <= m)
                        (f[i + k][j + k - 1] += f[i][j] * C[n - i - 1][k - 1] % P * fac[k] % P * inv2 % P) %= P;
                    if (k > 2 && j + k <= m)
                        (f[i + k][j + k] += f[i][j] * C[n - i - 1][k - 1] % P * fac[k - 1] % P * inv2 % P) %= P;
                }
            }
        }
        return f[n][m];
    }
    int main() {
        C[0][0] = fac[0] = 1;
        for (int i = 1; i <= 1000; i++) {
            C[i][0] = 1, fac[i] = fac[i - 1] * i % P;
            for (int j = 1; j <= i; j++) C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % P;
        }
        scanf("%d%d%d", &n, &m, &MX);
        if (MX == 1)
            return puts("0"), 0;
        // cout << get(MX) << ' ' << get(MX - 1) << endl;
        printf("%lld", (get(MX) - get(MX - 1) + P) % P);
    }
    

    纠结的数

    题意

    求第 \(n\) 小的正整数 \(X\) ,满足 \(X\) 的最小素因子为 \(P\) ,若 \(X\ge 10^9\) ,输出 0

    sol

    • \(n=1\) 则直接输出 \(p\) ,否则答案最小为 \(p^2\) ,若不为 0 则 \(p\le \sqrt{10^9}\) ,范围缩小

    运用到了拼盘的思路

    1. \(p\ge100\) 可以用小于 \(P\) 的质数暴力标记 \(P\) 的倍数,

      复杂度为 \(\dfrac{10^9}{2p}+\dfrac{10^9}{3p}+\dfrac{10^9}{5p}\cdots\) ,略小于 \(\ln \dfrac{10^9}{p}\)

    2. \(p< 100\) 时,暴力的复杂度不可取,但是 100 以内只有 25 个质数

      可以二分 \(p\) 的倍数 \(X\) ,求出能被小于 \(p\) 的素数整除的数,可以容斥解决

      复杂度 \(O(2^{25}\log 10^9)\)

    code

    #include <bits/stdc++.h>
    using namespace std;
    typedef unsigned long long uLL;
    typedef long double LD;
    typedef long long LL;
    typedef double db;
    const int INF = 1e9;
    const int SQ = 31623;
    int n, P, mx, ans, tmp, mid;
    bitset<20000000> mark;
    int pr[SQ + 5], cnt, vis[SQ + 5];
    void dfs(int i, int s, int op) {
        if (pr[i] == P) {
            tmp += op * mid / s;
            return;
        }
        dfs(i + 1, s, op);
        if (s <= mid / pr[i])
            dfs(i + 1, s * pr[i], -op);
    }
    int main() {
        for (int i = 2; i <= SQ; i++) {
            if (!vis[i])
                pr[++cnt] = i;
            for (int j = 1; j <= cnt && i * pr[j] <= SQ; j++) {
                vis[i * pr[j]] = 1;
                if (i % pr[j] == 0)
                    break;
            }
        }
        scanf("%d%d", &n, &P);
        if (n == 1)
            return printf("%d", P), 0;
        mx = INF / P;
        if (P > mx)
            return puts("0"), 0;
        if (P >= 100) {
            for (int i = 1; i <= cnt && pr[i] < P; i++)
                for (int j = pr[i]; j <= mx; j += pr[i]) mark[j] = 1;
            for (int i = 1, tc = 0; i <= mx; i++) {
                if (!mark[i])
                    ++tc;
                if (tc == n) {
                    ans = i * P;
                    break;
                }
            }
            printf("%d", ans);
        } else {
            int l = 2, r = mx + 1, res;
            while (l <= r) {
                mid = l + r >> 1;
                tmp = 0, dfs(1, 1, 1);
                if (tmp >= n)
                    r = mid - 1, res = mid;
                else
                    l = mid + 1;
            }
            printf("%d", res > mx ? 0 : res * P);
        }
    }
    

    删牌游戏

    题意

    \(3\times n\) 张牌,牌上数字为 \(A_i\) ,删 \(n-1\) 次牌

    每次从左边 5 张任意删去 3 张,若这 3 张牌数字相同可以得 1 分

    最后 3 张牌数字相同可以再得 1 分,求最大得分

    \(n\le 2000\)

    sol

    \(dp_{i,x,y}\) 为第 \(i\) 轮时之前剩下 \(x,y\) 的最大得分,此时若三、四、五张牌为 \(A,B,C\)

    \(f_{i+1,x',y'}=\max f_{i,x,y}+eq\) ,其中 \(eq\) 表示剩下 3 张是否相等, \(x',y'\) 表示在 \(x,y,A,B,C\) 中任选 2 张

    一次转移 \(C_5^2\) ,总 \(O(C_5^2 n^3)\) ,光荣 TLE


    可以压掉 \(i\) ,设状态为 \(f_{x,y}\) 表示最左边剩下 \(x,y\) 时的最大得分

    状态无法压缩,需要分类讨论,减少枚举的状态数

    1. \(x',y'\) 就是 \(x,y\) ,若 \(A=B=C\) ,答案整体加 1,可用一个变量 \(plus\) 记录

    2. \(x',y'\) 有一个是 \(A,B,C\) 中的值,假设 \(x'=x,y'=A\) (最多 6 种情况)

      \(f_{x',y'}=f_{x',A}=\max(f_{x',y}+eq)\) ,只需枚举 \(y\) 。预处理 \(\max dp_{x',y}\) ,复杂度 \(O(n)\)

    3. \(x',y'\) 都是 \(A,B,C\) 中的值,假设 \(x'=A,y'=B\) (最多 3 种情况)

      \(f_{x',y'}=f_{A,B}=\max (f_{C,C}+1,f_{x,y})\),复杂度为 \(O(1)\)

    4. 可用一个数组 \(g\) 辅助更新,每次 \(g_{x,y}\) 改变就存下 \((x,y)\) 之后用于改 \(f_{x,y}\)

      因为 \(g\) 每次改动最多 \(3\times 3n\) 次,这样可以避免枚举,使复杂度降到 \(O(n^2)\)

    答案直接枚举最后两个数,最后加上 \(plus\) 即可

    总共 \(O(n^2)\)

    code

    #include <bits/stdc++.h>
    using namespace std;
    typedef unsigned long long uLL;
    typedef long double LD;
    typedef long long LL;
    typedef double db;
    const int N = 2005;
    int n, vl[N * 3], pls, f[N][N], g[N][N], mx[N], tx[N], tt, le, xx[N * 9], yy[N * 9], ans = -1e9;
    inline void px(int &A, int &B, int &C) {
        if (A > B) A ^= B ^= A ^= B;
        if (A > C) A ^= C ^= A ^= C;
        if (B > C) B ^= C ^= B ^= C;
    }
    inline void push(int x, int y) {
        if (x > y) x ^= y ^= x ^= y;
        tx[x] = max(tx[x], g[x][y]);
        tx[y] = max(tx[y], g[x][y]);
        xx[++le] = x, yy[le] = y;
    }
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= 3 * n; i++) scanf("%d", &vl[i]);
        for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) f[i][j] = g[i][j] = -1e9;
        f[vl[1]][vl[2]] = f[vl[2]][vl[1]] = 0;
        mx[vl[1]] = mx[vl[2]] = 0;
        for (int o = 1, a, b, c; o < n; o++) {
            a = vl[3 * o], b = vl[3 * o + 1], c = vl[3 * o + 2];
            le = 0;
            if (a == b && b == c) { ++pls; continue; }
            px(a, b, c);
            for (int i = 1; i <= n; i++) tx[i] = mx[i];
            for (int i = 1, x; i < 3 * o; i++) {
                x = vl[i];
                g[x][a] = g[a][x] = max(mx[x], f[x][b] + (b == c)), push(x, a);
                g[x][b] = g[b][x] = max(mx[x], f[x][c] + (a == c)), push(x, b);
                g[x][c] = g[c][x] = max(mx[x], f[x][a] + (a == b)), push(x, c);
            }
            tt = -1e9;
            for (int i = 1; i <= n; i++) tt = max(tt, mx[i]);
            g[a][b] = max(tt, f[c][c] + 1), push(a, b);
            g[a][c] = max(tt, f[b][b] + 1), push(a, c);
            g[b][c] = max(tt, f[a][a] + 1), push(b, c);
            for (int i = 1; i <= n; i++) mx[i] = tx[i];
            for (int i = 1, x, y; i <= le; i++) {
                x = xx[i], y = yy[i];
                f[x][y] = f[y][x] = max(f[x][y], g[x][y]);
            }
        }
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++) ans = max(ans, f[i][j] + (i == j && j == vl[3 * n]));
        printf("%d", ans + pls);
    }
    
  • 相关阅读:
    一些关于"虚拟交易"的有趣文章
    Windows中的消息队列:Message Queuing (MSMQ)
    ATLStyle模板 不用虚函数实现多态
    AIX 下获取系统CPU及内存的使用情况方法
    关于HPUNIX C 兼容性
    Android进阶Acticivity的启动模式
    ViewState & UpdatePanle & ReadOnly属性
    由网站构架演变说起
    '操作必须使用一个可更新的查询'解决方法
    ScriptManager & ClientScriptManager
  • 原文地址:https://www.cnblogs.com/KonjakLAF/p/16150477.html
Copyright © 2020-2023  润新知