• 某【并不能AC的】模拟题部分解题报告


    题目来源:清北学堂钟皓曦

    D1T3 : 党

    【问题描述】

    你现在希望组建一支足球队,一支足球队一般来说由11人组成。这11人有四种不同的职业:守门员、后卫、中锋、前锋组成。你在组队的时候必须满足以下规则:

    1. 足球队恰好由11人组成。
    2. 11人中恰好有一名守门员,3-5 名后卫,2-5 名中锋,1-3 名前锋。
    3. 你需要从这11人中选出一名队长。
    4. 你这个足球队的价值是11人的价值之和再加上队长的价值,也就是说队长的价值会被计算两次
    5. 你这个足球队的花费是11人的花费之和,你的花费之和不能超过给定的上限。

    现在告诉你球员的总数,每个球员的职业、价值、花费,以及花费的上限,你希望在满足要求的情况下,达到以下目标:

    1. 最大化队伍的价值。
    2. 在最大化队伍的价值的情况下,最小化队伍的花费。

    你的任务是输出这三个值:价值、花费、方案数。

    【输入格式】

    第一行一个正整数N,代表可选的球员个数。
    接下来N行,每行描述一个球员的信息。每行开始是一个字符串,可能的字符串有 Goalkeeper、Defender、Midfielder、Forward,分别代表该球员的职业是守门员、后卫、中锋、前锋。接下来两个数V,C,分别代表该球员的价值和花费。
    最后一行一个整数,代表花费的上限。
    数据保证一定存在一种解。

    【输出格式】

    一行三个整数,分表代表最大价值、最小花费和方案数。如果方案数超过了109,则直接输出109

    【样例输入】

    15 
    Defender 23 45 
    Midfielder 178 85 
    Goalkeeper 57 50 
    Goalkeeper 57 50 
    Defender 0 45 
    Forward 6 60 
    Midfielder 20 50 
    Goalkeeper 0 50 
    Midfielder 64 65 
    Midfielder 109 70 
    Forward 211 100 
    Defender 0 40 
    Defender 29 45 
    Midfielder 57 60 
    Defender 52 45 
    600 

    【样例输出】

    716 600 2 

    【样例解释】

    选择所有的五名后卫,选择价值为178,20,64,109的中锋和价值为6的前锋,两名守门员任意选择。选择价值为178的中锋作为队长。

    【数据规模与约定】

    • 对于30%的数据,N20
    • 对于60%的数据,费用上限足够大。
    • 对于100%的数据,1N500,所有球员的价值和花费以及花费上限均在[0,1000]

    问题分析

    其实题目描述和数据不符。对于100%的数据,费用上限都不必考虑。以下算法均按此前提。

    首先,可以枚举阵容的所有可能人数分配,对于每种位置,注定要选择价值最大,花费最小的前几个人。所以对于不同种类的球员,分别按照价值降序,花费升序排序后直接取前若干人即可。然后问题只剩下了种类。不难发现,某一阵容要做等价替换,只可能替换当前阵容中价值最小,花费最大的人。记与价值最小,花费最大的人相同的人共有n人,其中需要选择m人,则该阵容的方案总数为:

    C(n,m)

    只需要注意把总价值-花费相同的所有阵容统计在一起即可AC。

    弱渣代码

    #include <bits/stdc++.h>
    using namespace std;
    
    struct player {
        int val, cost;
        friend bool operator < (player a, player b) {
            if (a.val == b.val)
                return a.cost < b.cost;
            return a.val > b.val;
        }
        friend bool operator == (player a, player b) {
            return a.val == b.val && a.cost == b.cost;
        }
    }G[505], D[505], M[505], F[505]; 
    
    int g = 0, d = 0, m = 0, f = 0;
    int n;
    string str;
    int val = -1, cost = 10000000;
    long long num = 0;
    
    inline long long C(int n, int m)
    {
        long long ans = 1;
        for (register int i = n; i >= n-m+1; i--)
            ans *= i;
        for (register int i = m; i; i--)
            ans /= i;
        return ans;
    }
    
    inline long long ways(player pl[], int stp, int endp)
    {
        int bef = 0, aft = 0;
        for (int i = stp; i; i--)
            if (pl[i] == pl[stp])
                bef++;
            else 
                break;
        for (int i = stp; i <= endp; i++)
            if (pl[i] == pl[stp])
                aft++;
            else
                break;
        return C(bef+aft-1, bef);
    }
    
    void sch(int gn, int dn, int mn, int fn)
    {
        int maxp1, maxp2, maxp3, maxp4;
        int minc1, minc2, minc3, minc4;
        maxp1 = maxp2 = maxp3 = maxp4 = 
        minc1 = minc2 = minc3 = minc4 = 0;
        for (int i = 1; i <= gn; i++) 
            maxp1 += G[i].val, minc1 += G[i].cost;
        for (int i = 1; i <= dn; i++) 
            maxp2 += D[i].val, minc2 += D[i].cost;
        for (int i = 1; i <= mn; i++) 
            maxp3 += M[i].val, minc3 += M[i].cost;
        for (int i = 1; i <= fn; i++) 
            maxp4 += F[i].val, minc4 += F[i].cost;
        int totp = maxp1+maxp2+maxp3+maxp4, totc = minc1+minc2+minc3+minc4;
        totp += max(D[1].val, max(G[1].val, max(M[1].val, F[1].val)));
        if (val < totp || (val == totp && cost > totc)) {
            val = totp;
            cost = totc;
            num = ways(G, gn, g)*ways(D, dn, d)*ways(M, mn, m)*ways(F, fn, f);
        } else if (val == totp && cost == totc) {
            num += ways(G, gn, g)*ways(D, dn, d)*ways(M, mn, m)*ways(F, fn, f);
        }
    }
    
    int main()
    {
        freopen("wosa.in", "r", stdin);
        freopen("wosa.out", "w", stdout);
        cin >> n; 
        for (int i = 1; i <= n; i++) {
            int a, b;
            cin >> str >> a >> b;
            if (str == "Goalkeeper") 
                G[++g] = {a, b};
            if (str == "Defender")
                D[++d] = {a, b};
            if (str == "Midfielder")
                M[++m] = {a, b};
            if (str == "Forward")
                F[++f] = {a, b};
        }
        sort(G+1, G+g+1);
        sort(D+1, D+d+1);
        sort(M+1, M+m+1);
        sort(F+1, F+f+1);
        for (int i = 3; i <= min(d, 5); i++)
            for (int j = 2; j <= min(5, m); j++)
                for (int k = 1; k <= min(3, f); k++)
                    if (i+j+k == 10) 
                        sch(1, i, j, k);
        cout << val << " " << cost << " " << min(num, 1000000000ll) << endl;
        return 0;
    }

    D2T3 : 三部曲

    【问题描述】

    因为外来的入侵,国王决定在某些城市加派士兵。所有城市初始士兵数量为 0。当城市 i 被加派了 k 名士兵时。城市i的所有子城市需要被加派 k+1 名士兵。这些子城市的所有子城市需要被加派 k+2 名士兵。以此类推。 当然,加派士兵的同时,国王也需要不断了解当前的情况。于是他随时可能询问以城市 i 为根的子树中的所有城市共被加派了多少士兵。 你现在是国王的军事大臣,你能回答出国王的每个询问么?

    【输入格式】

    第一行,包含两个整数 N , P 代表城市数量以及国王的命令的数量。 第二行 N − 1 个整数,表示2 - N 号每个节点的父亲节点。 接下来的P行,每行代表国王的一个命令,命令分两种: A X K 在城市X加入K个士兵;Q X 询问以城市X为根的子树中所有士兵数量的和。

    【输出格式】

    对于每个Q,输出答案。

    【样例输入】

    7 10 
    1 1 2 2 5 5 
    Q 1 
    A 2 1 
    Q 1 
    Q 2 
    Q 5 
    A 5 0 
    Q 5 
    A 3 1 
    Q 1 
    Q 2 

    【样例输出】

    0 
    11 
    11 
    8 
    10
    14 
    13 

    【样例解释】

    无。

    【数据规模与约定】

    • 对于50%的数据,1N1000,1P300
    • 对于100%的数据,1N50000,1P1000001XN,0K1000
    • 事实上,还应该补充一个条件:对于90%的数据,保证是随机生成的。笔者注。

    分析

    问题的第一步:已知某一结点的命令,求这一节点及孩子兵力变化的总量。记节点nd的孩子数和所有孩子距离nd的深度和分别为son[nd],sigma[nd],不难发现,一次命令(nd,num)造成兵力的增加总量为:

    ΔArmy=son[nd]×num+sigma[nd]()

    而这两个数组都可以在预处理时方便的求得:

    void dfs(int nd)
    {
            son[nd] = 1;
            for (int i = head[nd]; i; i = edge[i].next) {
                    dfs(edge[i].to);
                    son[nd] += son[edge[i].to];
                    sigma[nd] += sigma[edge[i].to]+son[edge[i].to];
            }
    }

    第二步优化复杂度:显然,朴素的O(NP)做法只能拿到50分,对于更大的数据就无能为力了。思考题目:一个明显的提示是当前城市k,子城市k+1,子城市的子城市k+2……,这个下推的过程显然是导致时间复杂度偏大的重要原因。什么能避免下推耗时呢?不难想到Lazy Tag思想。所谓Lazy Tag,就是当一个数据结构存在两种操作:Modify和Query时,如果Modify的复杂度较大,且Modify操作对于Query结果影响可以快速的确定,就可以使用Lazy Tag来代替直接修改。1

    更进一步解释,就是维护的树的并不是简单的存下当前节点的兵力,而是维护三个值:Army[i],TagArmy[i],TagTime[i]分别表示当前节点及子节点的已调动的兵力,当前结点标记的未执行命令,和当前节点标记的未执行命令的次数。形象一点说,就是国王的军队并不负责,只在国王检查时调动兵力,且只调动国王要检查的兵力大概和我等写作业有异曲同工之妙),这样就节省了大量的时间。

    具体的操作是:

    inline void AddArmy(int nd, int num)
    {
            tag_army[nd] += num;
            tag_time[nd]++;
            long long delta = son[nd]*num+sigma[nd];
            // 祖先节点的兵力增加值,见(*)
            for (int k = fa[nd]; k; k = fa[k])
                    army[k] += delta;
            // 其祖先节点的兵力增加
    } // 只加标记,复杂度为O(h),h为树高
    
    inline int Query(int nd)
    {
            if (nd == 0) return 0;
            // 到达树根(树根的父亲设为0)
            Query(fa[nd]);
            // 递归,要求下传父亲和祖先节点的所有标记
            for (int i = head[nd]; i; i = edge[i].next) {
                    int to = edge[i].to;
                    tag_army[to] += tag_army[nd] + tag_time[nd];
                    tag_time[to] += tag_time[nd];
            }
            // 下传当前结点的标记给所有孩子,注意
            /*
            tag_army[to] += tag_army[nd] + tag_time[nd];
            下传的兵力要加上命令的次数
            */
            if (tag_time[nd] > 0)
                    army[nd] += tag_army[nd]*son[nd]+sigma[nd]*tag_time[nd];
            tag_time[nd] = 0;
            tag_army[nd] = 0;
            // 如果当前节点有完成下传的标记,删除之,并记录新的部队总数
            return army[nd];
    } // O(h)

    在期望情况下(树是近似平衡的),算法的复杂度为O(PlgN)。但一个问题是特殊数据(链)会导致这种算法超时,即达到O(PN)的最坏复杂度。对此其实有一种利用dfs序建立线段树,用区间修改处理的方法可以AC,Θ(PlgN)。不过显然,这样的方法从思维建立上更“竞赛”,更“优美”一点。

    并不AC的示例代码

    #include <bits/stdc++.h>
    using namespace std;
    
    int fa[50005];
    struct p {
            int to, next;
    }edge[60005];
    int head[50005], top = 0;
    void push(int i, int j)
    {
            edge[++top].to = j;
            edge[top].next = head[i];
            head[i] = top;
    }
    long long son[50005], sigma[50005]; //num of son(including himself), sigma of depth
    long long tag_army[50005], tag_time[50005]; // lazy tag
    long long army[50005];
    int N, P;
    inline void init()
    {
            memset(fa, 0, sizeof fa);
            memset(head, 0, sizeof head);
            memset(son, 0, sizeof son);
            memset(sigma, 0, sizeof sigma);
            memset(tag_army, 0, sizeof tag_army);
            memset(tag_time, 0, sizeof tag_time);
            memset(army, 0, sizeof army);
    }
    
    void dfs(int nd)
    {
            son[nd] = 1;
            for (int i = head[nd]; i; i = edge[i].next) {
                    dfs(edge[i].to);
                    son[nd] += son[edge[i].to];
                    sigma[nd] += sigma[edge[i].to]+son[edge[i].to];
            }
    }
    
    inline void AddArmy(int nd, int num)
    {
            tag_army[nd] += num;
            tag_time[nd]++;
            long long delta = son[nd]*num+sigma[nd];
            for (int k = fa[nd]; k; k = fa[k])
                    army[k] += delta;
    } // just add tag
    
    inline int Query(int nd)
    {
            if (nd == 0) return 0;
            Query(fa[nd]);
            for (int i = head[nd]; i; i = edge[i].next) {
                    int to = edge[i].to;
                    tag_army[to] += tag_army[nd] + tag_time[nd];
                    tag_time[to] += tag_time[nd];
            }
            if (tag_time[nd] > 0)
                    army[nd] += tag_army[nd]*son[nd]+sigma[nd]*tag_time[nd];
            tag_time[nd] = 0;
            tag_army[nd] = 0;
            return army[nd];
    }
    
    int main()
    {
            freopen("truetears.in", "r", stdin);
            freopen("truetears.out", "w", stdout);
            init();
            scanf("%d%d", &N, &P);
            for (int i = 2; i <= N; i++) {
                    scanf("%d", &fa[i]);
                    push(fa[i], i);
            }
            dfs(1);
            for (int i = 1; i <= P; i++) {
                    char cmd;
                    int a, b;
                    //scanf("%c", &cmd);
                    cin >> cmd;
                    if (cmd == 'A') {
                            scanf("%d%d", &a, &b);
                            AddArmy(a, b);
                    } else {
                            scanf("%d", &a);
                            //cin >> a;
                            printf("%d
    ", Query(a));
                    }
            }
        return 0;
    }
  • 相关阅读:
    P1772 [ZJOI2006]物流运输
    P3951 小凯的疑惑
    P1082 同余方程(【模板】exgcd)
    T107741 【模板】权值线段树合并
    P3205 [HNOI2010]合唱队
    P1062 数列
    P1144 最短路计数
    P1502 窗口的星星
    P4147 玉蟾宫(【模板】悬线法)
    CSP模拟赛#3 分段(T1-26)
  • 原文地址:https://www.cnblogs.com/ljt12138/p/6684369.html
Copyright © 2020-2023  润新知