• codeforce刷题(四)


    CodeForces 1320C World of Darkraft: Battle for Azathoth

    (n)个武器(m)个盾牌,无论是武器还是盾牌都有相应的攻击值(防御值)和价格,同时还有(p)只怪物,每只怪物都有防御值、攻击值、和干掉它能有多少钱。你只能选择一件武器和一件盾牌去,问最终通过打怪获得最多的钱是多少(减去买武器和盾牌的花费后)?

    题解

    最朴素的想法是(O(nmp)),显然不行。那怎么入手呢?只考虑一维的话,对武器和怪物分别排序后,二分加算后缀就能解决。现在多了一维:盾牌和怪兽的攻击,有点难办了。对于每一件武器,假如它能杀死(仅仅只是武器的攻击值大于怪兽的防御值)的怪兽集合是(S),那么怎么求集合(S)中能获得的最大收益呢?建一颗线段树,用来维护集合(S)的最大收益,每加入一个怪兽,假如它的攻击值为(x),那么就更新大于(x)的那部分的最大值。举例来说:令(a[i] :=) 防御值为 (i) 的盾牌在集合S中能帮助您取得的最大收益,加入一个攻击值为(x)的怪兽,就更新:(a[x + 1] += coins, a[x + 2] += coins, ···),然后取它们的最大值(max(a))就是集合(S)的最大收益。

    当盾牌的防御值相同时,肯定选价格较低的那个盾牌,也就是在建树的函数中:ma[root] = max(ma[root], -b.price)

    struct monster {
        int x, y, z;
        bool operator < (const monster o) const {
            return x < o.x;
        }
    };
    
    int n, m, p, cnt;
    long long ma[1000005 * 4], laz[1000005 * 4];
    
    void Build_Tree(int l, int r, int root, int pos, int x) {
        if ((l == r) && (l == pos)) {
            ma[root] = max(ma[root], (long long)-x);  // 坑点1
            return;
        }
        int mid = (l + r) >> 1;
        if (pos <= mid) Build_Tree(l, mid, root << 1, pos, x);
        else Build_Tree(mid + 1, r, (root << 1) + 1, pos, x);
        ma[root] = max(ma[root << 1], ma[(root << 1) + 1]);
    }
    
    void Push_down(int root) {
        laz[root << 1] += laz[root];
        laz[(root << 1) + 1] += laz[root];
        ma[(root << 1)] += laz[root];
        ma[(root << 1) + 1] += laz[root];
        laz[root] = 0;
    }
    
    void Update_Tree(int L, int R, int l, int r, int root, int coins) {
        if (L > R) return;         // 坑点2
        if (L <= l && r <= R) {
            ma[root] += coins;
            laz[root] += coins;
            return;
        }
        if (laz[root]) Push_down(root);
        int mid = (l + r) >> 1;
        if (L <= mid) Update_Tree(L, R, l, mid, root << 1, coins);
        if (R > mid) Update_Tree(L, R, mid  + 1, r, (root << 1) + 1, coins);
        ma[root] = max(ma[root << 1], ma[(root << 1) + 1]);
    }
    
    int main()
    {
    
        scanf("%d %d %d", &n, &m, &p);
    
        vector<pair<int, int> > a(n), b(m);
        vector<struct monster > c(p);
    
        int length = 0;
    
        myfor(i, 0, n) scanf("%d %d", &a[i].first, &a[i].second);
        myfor(i, 0, m) scanf("%d %d", &b[i].first, &b[i].second), length = max(length, b[i].first);
        myfor(i, 0, p) scanf("%d %d %d", &c[i].x, &c[i].y, &c[i].z), length = max(length, c[i].y);
    
    
        sort(a.begin(), a.end());
        sort(c.begin(), c.end());
    
        myfor(i, 0, length * 4) ma[i] = -INF;
        myfor(i, 0, m) Build_Tree(1, length, 1, b[i].first, b[i].second);
    
        int index = 0;
        long long ans = -INF;
    
        myfor(i, 0, n) {
            while(index < p && c[index].x < a[i].first) {
                Update_Tree(c[index].y + 1, length, 1, length, 1, c[index].z);        // length = max(c.y), 所以可能会出现:c[index].y + 1 > length 
                index++;
            }
            ans = max(ans, ma[1] - a[i].second);
        }
    
        cout << ans << endl;
        return 0;
    }
    

    CodeForces 1320B Navigation System

    (n)个点,(m)条边, 组成一个有向图,然后再给一条路径: (p_1 ightarrow p_2 ightarrow p_3 ightarrow ··· ightarrow p_k)。有一个导航仪,当你走到(p_i)处,然后出发去下一个点(p_{i + 1}),如果(p_{i + 1}) 不是 (p_i)到终点(p_k)的最短路径上的点,则导航仪会重新规划一条从(p_{i + 1})(p_k) 的最短路径。如果是(p_i)到终点(p_k)的最短路径上的点且(p_i)到终点(p_k)的最短路径有多条,这时你选择去(p_{i + 1}),则导航仪可以重新规划也可以不重新规划。问最小规划次数和最大规划次数?

    题目有点绕,建议看原文:https://vjudge.net/problem/CodeForces-1320B

    题解

    从终点BFS一遍,求出从终点出发到其它所有点的最短距离,然后判断(p_{i + 1})是不是从(p_i)(p_k)的最短路径上的点:

    • 是,并且从(p_i)(p_k)的最短路径有多条,那么最大规划次数加一
    • 不是,说明在(p_{i + 1})处必须重新规划到(p_k)的最短路径,那么最大和最小规划次数都加一

    规划的最短路径可能会经过之前已经走到的点,举例来说:1-2,2-3,3-4,4-5,1-5,2-1,然后给出路径:1-2-3-4-5。当走到点2处,最短路径是2-1-5,而1已经走过。之所以会记录这个问题,是因为我标记了之前走过的点(被注释的两行代码),然后wa了。

    /* 998 ms, 算不来这样做的复杂度,忘大佬告知*/
    void BFS(int st) {
        myfor(i, 0, n) d[i] = INF;
    
        queue<int> q;
        q.push(st);
        d[st] = 0;
    
        while(!q.empty()) {
            int u = q.front();
            q.pop();
    
            for (int i = head_e[u]; i != -1; i = e[i].next) {
                int v = e[i].to;
                if (d[v] == INF) {
                    d[v] = d[u] + 1;
                    q.push(v);
                }
            }
        }
    }
    
    void Solve()
    {
        int ans_min = 0, ans_max = 0;
        //vector<int> use(n + 1, 0);
    
        myfor(i, 1, k - 1)
        {
            int u = p[i], t = 0;
            //use[u] =  1;  
            
            for (int o = head_E[u]; o != -1; o = E[o].next) {
                int v = E[o].to;
                //if (use[v]) continue;
                if (d[v] + 1 == d[u]) t++;      // 判断有几条最短路径
            }
    
            if (d[p[i + 1]] + 1 == d[u]) {
                if (t > 1) ans_max++;
            }
            else {
                ans_min++, ans_max++;
            }
        }
    
        cout << ans_min << " " << ans_max << endl;
    }
    
  • 相关阅读:
    SQL Server 2012 同步数据
    SQL md5 加密
    构建BootStrap
    js toFixed 真正四舍五入
    Angular BootStrap 登录页面
    Gitblit 安装使用
    js 函数
    git push error HTTP code = 413
    react-native 插件汇总
    js 监听组合键盘事件
  • 原文地址:https://www.cnblogs.com/zgglj-com/p/12676739.html
Copyright © 2020-2023  润新知