• 2018冬令营模拟测试赛(二)


    2018冬令营模拟测试赛(二)

    [Problem A]最小生成树

    试题描述

    TAT

    QAQ

    TAQ

    输入

    见“试题描述

    输出

    见“试题描述

    输入示例

    见“试题描述

    输出示例

    见“试题描述

    数据规模及约定

    见“试题描述

    题解

    这是一道坑了我的结论题(考场上推了半天结果得零分,最气的是发现再特判一下就 A 了!!!

    我们不妨让最终的最小生成树长成一条链,然后这条链的边权从左到右依次单调不降。

    那么为了构建出 (m) 条边的无向图,我们可以贪心地再左边加边,即 (t)(1) 开始计,每次将前 (t) 个点加边加成完全图,然后 (t)(1),直到 (t=n)(注意最开始要先把所有节点按顺序串成一条链)。

    那么一个安排边权的贪心策略来了:将前 (n-1) 个点之间连接的所有边的边权安排成 (1),然后剩下的边(即和第 (n) 个点连的所有边)边权安排成 (n-2)

    这样显然不是最优的,但这给我们提供了一个很好的思路,接下来的算法就是在上面这个建图基础上的。

    我们假设一开始所有边权都是 (0),那么有两种选择:一种是将所有边权 (+1),一种是将和最后一个点有关的所有边权 (+1);目标是让生成树的所有边之和 (=s),那么这两种选择就相当于两种物品,背包的容量为 (s),第一种体积为 (1),代价为 (x)(即和最后一个点有关的边数)(注意这里用代价是因为我们要最小化这个值,而“价值”是一个褒义词故不用它),第二种物品物品体积为 (n-1)代价(m)。那么假设我们选择 (a) 个第一种物品,(b) 个第二种物品,令 (N = n-1),则有 (a + bN = s),要最小化 (ax + bm) 的值;把 (a)(b) 表示得到最小化 ((s-bN)x + bm = sx - Nxb + mb = (m-Nx)b + sx),当 (m-Nx ge 0)(b = 0) 即可,否则让 (b) 取到最大,也即 (lfloor frac{s}{N} floor)。于是答案可以 (O(1)) 得到。

    当然不要忘掉一个我考场上漏掉的特殊情况。容易发现我们上面列出的两个并不是全部可选的物品,因为我们还可以选择后任意多个点之间的所有边 (+1)——只需要保证每时每刻那条链的边权单调不降就行。这个问题的解决方案是所有边取平均值 (frac{s}{m})(前面的边下取整,后面的边上取整)就好了。至于为啥,我不知道。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    #define rep(i, s, t) for(int i = (s); i <= (t); i++)
    #define dwn(i, s, t) for(int i = (s); i >= (t); i--)
    #define LL long long
    
    LL read() {
        LL x = 0, f = 1; char c = getchar();
        while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
        while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
        return x * f;
    }
    
    LL calc(int x) { return (LL)x * (x - 1) >> 1; }
    
    LL solve(int n, LL m, LL s, LL restm) {
        if(restm <= calc(n - 1)) return min(restm, calc(n - 1)) + s + 1;
        n--;
        restm -= calc(n); restm++;
        // printf("(1, %lld)
    (%d, %lld)
    s = %lld
    ", restm, n, m, s);
        if(m - restm * n >= 0) return s * restm + m;
        LL a = s, b = 0;
        LL k = a / n; b += k;
        return s * restm + b * (m - restm * n) + m;
    }
    LL avg(int n, LL m, LL s) {
        LL lo = s / (n - 1), hi = (s + n - 2) / (n - 1);
        if(lo == hi) return m * lo;
        n--;
        LL restm = m - 1 - calc(n) + 1, sma = lo * n + n - s, pre = min(calc(sma + 1) - sma + 1, m - n + 1) + sma - 1, suf = m - pre;
        return pre * lo + suf * hi;
    }
    
    int main() {
        int T = read();
    
        while(T--) {
            int n = read();
            LL m = read(), s = read() - n + 1, restm = m - 1;
            printf("%lld
    ", min(solve(n, m, s, restm), avg(n, m, s + n - 1)));
        }
    
        return 0;
    }
    

    [Problem B]没有上司的舞会

    试题描述

    Q_Q

    ToT

    输入

    见“试题描述

    输出

    见“试题描述

    输入示例

    见“试题描述

    输出示例

    见“试题描述

    数据规模及约定

    见“试题描述

    题解

    用数据结构维护 dp 信息。一句话解决问题。

    好吧还是讲讲,我们考虑用 LCT 维护动态加点的操作,每个节点 (i) 记录 (f_0(i))(f_1(i)) 分别表示不选和选择它自己最多能从它以及它所有 LCT 上“非访问边”中选择多少个节点。(access) 操作的时候直接对 (f_0)(f_1) 进行加减操作实现加入一个儿子和删掉一个儿子。此外在 splay 中搞一个 (F_{a,b}(i))(a, b) 都是 (0)(1) 的整数)表示对于 splay 中的子树 (i),首、尾选不选的 dp 值,这样就可以方便地合并了。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    #define rep(i, s, t) for(int i = (s); i <= (t); i++)
    #define dwn(i, s, t) for(int i = (s); i >= (t); i--)
    
    int read() {
        int x = 0, f = 1; char c = getchar();
        while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
        while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
        return x * f;
    }
    
    #define maxn 300010
    #define oo 2147483647
    
    struct LCT {
        int f[maxn][2], F[maxn][2][2];
        int fa[maxn], ch[maxn][2];
    
        bool isrt(int u) { return ch[fa[u]][0] != u && ch[fa[u]][1] != u; }
    
        void merge(int to, int l, int r) {
            int nf[2][2];
            nf[0][0] = max(max(F[l][0][1] >= 0 && F[r][0][0] >= 0 ? F[l][0][1] + F[r][0][0] : -1, F[l][0][0] >= 0 && F[r][1][0] >= 0 ? F[l][0][0] + F[r][1][0] : -1), F[l][0][0] >= 0 && F[r][0][0] >= 0 ? F[l][0][0] + F[r][0][0] : -1);
            nf[0][1] = max(max(F[l][0][1] >= 0 && F[r][0][1] >= 0 ? F[l][0][1] + F[r][0][1] : -1, F[l][0][0] >= 0 && F[r][1][1] >= 0 ? F[l][0][0] + F[r][1][1] : -1), F[l][0][0] >= 0 && F[r][0][1] >= 0 ? F[l][0][0] + F[r][0][1] : -1);
            nf[1][0] = max(max(F[l][1][1] >= 0 && F[r][0][0] >= 0 ? F[l][1][1] + F[r][0][0] : -1, F[l][1][0] >= 0 && F[r][1][0] >= 0 ? F[l][1][0] + F[r][1][0] : -1), F[l][1][0] >= 0 && F[r][0][0] >= 0 ? F[l][1][0] + F[r][0][0] : -1);
            nf[1][1] = max(max(F[l][1][1] >= 0 && F[r][0][1] >= 0 ? F[l][1][1] + F[r][0][1] : -1, F[l][1][0] >= 0 && F[r][1][1] >= 0 ? F[l][1][0] + F[r][1][1] : -1), F[l][1][0] >= 0 && F[r][0][1] >= 0 ? F[l][1][0] + F[r][0][1] : -1);
            memcpy(F[to], nf, sizeof(nf));
            return ;
        }
        void maintain(int u) {
            F[u][0][0] = f[u][0]; F[u][1][1] = f[u][1];
            F[u][0][1] = F[u][1][0] = -1;
            if(ch[u][0]) merge(u, ch[u][0], u);
            if(ch[u][1]) merge(u, u, ch[u][1]);
            return ;
        }
        void rotate(int u) {
            int y = fa[u], z = fa[y], l = 0, r = 1;
            if(!isrt(y)) ch[z][ch[z][1]==y] = u;
            if(ch[y][1] == u) swap(l, r);
            fa[u] = z; fa[y] = u; fa[ch[u][r]] = y;
            ch[y][l] = ch[u][r]; ch[u][r] = y;
            return maintain(y);
        }
        void splay(int u) {
            while(!isrt(u)) {
                int y = fa[u], z = fa[y];
                if(!isrt(y)) {
                    if(ch[y][0] == u ^ ch[z][0] == y) rotate(u);
                    else rotate(y);
                }
                rotate(u);
            }
            return maintain(u);
        }
    
        void access(int u) {
            splay(u);
            int fson[2];
            if(ch[u][1]) {
                fson[0] = max(F[ch[u][1]][0][0], F[ch[u][1]][0][1]);
                fson[1] = max(F[ch[u][1]][1][0], F[ch[u][1]][1][1]);
                f[u][0] += max(fson[0], fson[1]); f[u][1] += fson[0];
            }
            ch[u][1] = 0;
            maintain(u);
            while(fa[u]) {
                splay(fa[u]);
                if(ch[fa[u]][1]) {
                    fson[0] = max(F[ch[fa[u]][1]][0][0], F[ch[fa[u]][1]][0][1]);
                    fson[1] = max(F[ch[fa[u]][1]][1][0], F[ch[fa[u]][1]][1][1]);
                    f[fa[u]][0] += max(fson[0], fson[1]); f[fa[u]][1] += fson[0];
                }
                fson[0] = max(F[u][0][0], F[u][0][1]);
                fson[1] = max(F[u][1][0], F[u][1][1]);
                f[fa[u]][0] -= max(fson[0], fson[1]); f[fa[u]][1] -= fson[0];
                ch[fa[u]][1] = u;
                maintain(fa[u]);
                splay(u);
            }
            return ;
        }
        void link(int a, int b) { // b should be a leaf
            f[b][0] = 0; f[b][1] = 1; maintain(b);
            access(a); fa[b] = a; ch[a][1] = b;
            maintain(a);
            return ;
        }
    } sol;
    
    int main() {
        int n = read() + 1, type = read(), lstans = 0;
        // printf("%d %d
    ", n, type);
        sol.f[1][0] = 0; sol.f[1][1] = 1; sol.maintain(1);
        rep(i, 2, n) {
            int fa = (read() ^ type * lstans) + 1;
            sol.link(fa, i);
            sol.access(1);
            printf("%d
    ", lstans = max(sol.f[1][0], sol.f[1][1]));
        }
    
        return 0;
    }
    

    [Problem C]排列问题

    试题描述

    QwQ

    T_T

    TwT

    输入

    见“试题描述

    输出

    见“试题描述

    输入示例

    见“试题描述

    输出示例

    见“试题描述

    数据规模及约定

    见“试题描述

    题解

    我去这题有点毒瘤。

    首先你需要想到容斥 dp,其它 dp 方式都无法搞定正解。

    (f(i, j)) 表示对于前 (i) 种颜色,序列强行分了 (j) 段的方案数,那么转移就简单了,枚举最后一种颜色分多少段即可,然后推式子搞搞成为卷积的形式:(f(i, j) = sum_{k=1}^{mathrm{min}{ a_i, j }} { f(i-1, j-k) cdot C_j^k cdot C_{a_i-1}^{k-1} } = sum_{k=1}^{mathrm{min}{ a_i, j }} { f(i-1, j - k) cdot frac{j!}{k!(j-k)!} cdot frac{(a_i-1)!}{(k-1)!(a_i-k)!} } Rightarrow frac{f(i, j)}{j!} = sum_{k=1}^{mathrm{min}{ a_i, j }} { frac{f(i-1, j-k)}{(j-k)!} cdot frac{(a_i-1)!}{k!(k-1)!(a_i-k)!} })

    于是令 (A_i(x) = sum_{k=1}^{a_i} { frac{(a_i - 1)!}{k!(k-1)!(a_i-k)!} cdot x^k })(F_0(x) = 1),那么得到的 (F_n(x) = F_0(x) cdot prod_{i=1}^n {A_i(x)}) 所有系数就是最后的 (f(n, i) cdot i!),由于乘法有结合律,可以先分治 FFT 求出后面那一坨 (A_i(x)) 乘起来的多项式,然后再用 (F_0(x)) 去乘就好了。

    然后我们还要容斥,显然上面会重复计算,上面的 dp 会对球序列多分几段,所以要依次减掉多分一段的,加上多分两段的,减去多分三段的……形式化地:令 (forall i in [1, m], g(i) = f(n, i)),最终分 (i) 段的方案数为 (Ans_i),则 (Ans_i = sum_{j=1}^i { (-1)^{i-j} cdot g(j) cdot C_{m-j}^{i-j} } Rightarrow Ans_i cdot (m-i)! = sum_{j=1}^i {g(j) cdot (m-j)! cdot frac{(-1)^{i-j}}{(i-j)!} }),还是卷积!再 FFT 一下就好了。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    #include <vector>
    using namespace std;
    #define rep(i, s, t) for(int i = (s); i <= (t); i++)
    #define dwn(i, s, t) for(int i = (s); i >= (t); i--)
    
    int read() {
        int x = 0, f = 1; char c = getchar();
        while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
        while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
        return x * f;
    }
    
    #define maxn 524288
    #define MOD 998244353
    #define Groot 3
    #define LL long long
    
    int rev[maxn];
    int Pow(int a, int b) {
        int ans = 1, t = a;
        while(b) {
            if(b & 1) ans = (LL)ans * t % MOD;
            t = (LL)t * t % MOD; b >>= 1;
        }
        return ans;
    }
    void FFT(vector <int> &A, int len, int tp) {
        int n = 1 << len;
        rep(i, 0, n - 1) if(i < rev[i]) swap(A[i], A[rev[i]]);
        rep(i, 1, len) {
            int wn = Pow(Groot, MOD - 1 >> i);
            if(tp < 0) wn = Pow(wn, MOD - 2);
            for(int j = 0; j < n; j += 1 << i) {
                int w = 1;
                rep(k, 0, (1 << i >> 1) - 1) {
                    int al = A[j+k], ar = (LL)w * A[j+k+(1<<i>>1)] % MOD;
                    A[j+k] = (al + ar) % MOD;
                    A[j+k+(1<<i>>1)] = (al - ar + MOD) % MOD;
                    w = (LL)w * wn % MOD;
                }
            }
        }
        if(tp < 0) rep(i, 0, n - 1) A[i] = (LL)A[i] * Pow(n, MOD - 2) % MOD;
        return ;
    }
    void Mul(vector <int> &A, vector <int> &B) {
        int n = A.size() - 1, N = 1, len = 0;
        while(N <= n) N <<= 1, len++;
        rep(i, 1, N - 1) rev[i] = (rev[i>>1] >> 1) | ((i & 1) << len - 1);
        FFT(A, len, 1); FFT(B, len, 1);
        rep(i, 0, N - 1) A[i] = (LL)A[i] * B[i] % MOD;
        FFT(A, len, -1);
        return ;
    }
    
    int m, a[maxn], fac[maxn], ifac[maxn];
    
    vector <int> GetTrans(int l, int r) {
        if(l == r) {
            vector <int> A(a[l] + 1);
            A[0] = 0;
            rep(i, 1, a[r]) A[i] = (LL)fac[a[l]-1] * ifac[i] % MOD * ifac[i-1] % MOD * ifac[a[r]-i] % MOD;
            return A;
        }
        int mid = l + r >> 1;
        vector <int> A = GetTrans(l, mid), B = GetTrans(mid + 1, r);
        int asiz = A.size(), bsiz = B.size(), siz = asiz + bsiz, need = 1;
        while(need <= siz - 2) need <<= 1;
        A.resize(need); B.resize(need);
        rep(i, asiz, need - 1) A[i] = 0; rep(i, bsiz, need - 1) B[i] = 0;
        Mul(A, B);
        vector <int> E(siz-1);
        rep(i, 0, siz - 2) E[i] = A[i];
        return E;
    }
    
    int main() {
        int n = read();
        rep(i, 1, n) a[i] = read(), m += a[i];
    
        fac[0] = ifac[0] = 1;
        rep(i, 1, m) fac[i] = (LL)fac[i-1] * i % MOD, ifac[i] = (LL)ifac[i-1] * Pow(i, MOD - 2) % MOD;
    
        vector <int> A = GetTrans(1, n), F;
        int siz = A.size(), need = 1;
        while(need <= siz - 1) need <<= 1;
        F.resize(need); F[0] = 1; rep(i, 1, need - 1) F[i] = 0;
        A.resize(need); rep(i, siz, need - 1) A[i] = 0;
        Mul(F, A);
        rep(i, 0, siz - 1) F[i] = (LL)F[i] * fac[i] % MOD;
        // printf("F[0~%d]: ", m); rep(i, 0, m) printf("%d%c", F[i], i < m ? ' ' : '
    ');
    
        need = 1;
        while(need <= (m << 1)) need <<= 1;
        vector <int> G(need), T(need);
        G[0] = 0; rep(i, 1, m) G[i] = (LL)F[i] * fac[m-i] % MOD; rep(i, m + 1, need - 1) G[i] = 0;
        // rep(i, 0, m) printf("%d%c", G[i], i < m ? ' ' : '
    ');
        rep(i, 0, m) T[i] = (i & 1) ? MOD - ifac[i] : ifac[i]; rep(i, m + 1, need - 1) T[i] = 0;
        Mul(G, T);
        rep(i, 0, m) G[i] = (LL)G[i] * ifac[m-i] % MOD;
    
        int q = read();
        while(q--) {
            int k = read();
            printf("%d
    ", G[m-k]);
        }
    
        return 0;
    }
    
  • 相关阅读:
    POJ 1751 Highways (kruskal)
    POJ 2031 Building a Space Station
    UVA 624
    POJ 1502 MPI Maelstrom (Dijkstra)
    POJ 3259 Wormholes(SPFA判负环)
    HZAU 1199 Little Red Riding Hood(水DP)
    HZAU 1205 Sequence Number(最大值前后缀 +双指针 + 二分)
    HZAU 1209 Deadline (hash 贪心 水题不水)
    STL完整版整理
    set集合完整版整理
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8085260.html
Copyright © 2020-2023  润新知