CF1321-World of Darkraft: Battle for Azathoth (线段树+二维偏序)

题意:

题目大致意思是给你n把武器,m件防具,p个怪兽,接下来n行每行告诉你该武器的攻击力和花费,

接下来m行告诉你该防具的防御力和花费,然后p行每行告诉你这个怪兽的攻击力,防御力以及打败这个

怪兽可以获得的金钱数,当你的攻击力大于怪兽的防御力,并且你的防御力大于怪兽的攻击力时,你可

以打败这个怪兽。你必须至少购买1件武器和1件防具,问你最多可以获得多少钱。

链接:https://codeforces.com/contest/1321/problem/E

思路:

看了大神的题解,第一次知道二维偏序这种东西,然后可以用线段树解决这类问题。

我们先将武器按照攻击力从小到大排序,再将防具按照防御力从小到大排序,然后将

怪兽按照防御力从小到大排序,对防具建立线段树,每个叶子节点的初始值是使用该

防具的花费,线段树的每个节点存该区间内能获得的最大收益,至于区间的更新,

在当前武器的攻击力大于该怪物的防御力的情况下,要想打败这个怪物,那么防具的

防御力必须大于怪物的攻击力,也就是说,我们要在防具中找到第一个防御力比怪物的

攻击力大的位置 index ,然后更新[index,m]这段区间,区间内的每个点加上击败怪物

的收益,代表在使用i这件武器的情况下,用这段区间内的任何一件防具均可击败该怪物

。最后,我们只需要用tree[1](利润-防具的花费的最大值)-当前的武器的花费,就能得出在这种情况下的收益。

代码如下

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int INF = 0x7fffffff;
typedef long long ll;
int tree[MAXN<<2],lazy[MAXN<<2],n,m,p;
struct node
{
    int ability,cost;
    node() {}
    node(int a,int b) : ability(a),cost(b) {}
    bool operator <(node temp)const
    {
        return ability<temp.ability;
    }
} a[MAXN],b[MAXN];
struct monster
{
    int defend,attack,gain;
    monster() {}
    monster(int a,int b,int c) : attack(b),defend(a),gain(c) {}
    bool operator < (monster temp)const
    {
        return defend<temp.defend;
    }
} c[MAXN];// 线段树查询区间最大值  
void push_up(int node)
{
    tree[node]=max(tree[node<<1],tree[node<<1|1]);
}
void build(int node,int l,int r)
{
    //lazy[node]=0;
    if(l==r)
    {
        tree[node]=-b[l].cost;
        return;
    }
    int mid=(l+r)>>1;
    build(node<<1,l,mid);
    build(node<<1|1,mid+1,r);
    push_up(node);
}
void push_down(int node)
{
    if(lazy[node])
    {
        tree[node<<1]+=lazy[node];
        tree[node<<1|1]+=lazy[node];
        lazy[node<<1]+=lazy[node];
        lazy[node<<1|1]+=lazy[node];
        lazy[node]=0;
    }
}
void update(int node,int l,int r,int x,int y,int k)
{
    if(x<=l&&y>=r)
    {
        tree[node]+=k;
        lazy[node]+=k;
        return;
    }
    push_down(node);
    int mid=(l+r)>>1;
    if(x<=mid)
        update(node<<1,l,mid,x,y,k);
    if(y>mid)
        update(node<<1|1,mid+1,r,x,y,k);
    push_up(node);
}
int main()
{
    scanf("%d%d%d",&n,&m,&p);
    for(int i=1; i<=n; i++)
        scanf("%d%d",&a[i].ability,&a[i].cost);
    for(int i=1; i<=m; i++)
        scanf("%d%d",&b[i].ability,&b[i].cost);
    for(int i=1; i<=p; i++)
        scanf("%d%d%d",&c[i].defend,&c[i].attack,&c[i].gain);
    sort(a+1,a+1+n);
    sort(b+1,b+1+m);
    sort(c+1,c+1+p);
    build(1,1,m);
    int now=1;
    int ans=-INF;
    for(int i=1; i<=n; i++)
    {
        while(now<=p&&c[now].defend<a[i].ability)
        {
            //printf("%d\n",c[now].attack);
            int index=upper_bound(b+1,b+1+m,node(c[now].attack,0))-b;//找出第一个大于这个怪兽攻击力的位置
            if(index<=m)
            {
                update(1,1,m,index,m,c[now].gain);//区间更新;
            }
            now++;
        }
        ans=max(ans,tree[1]-a[i].cost);
    }
    printf("%d\n",ans);
}

原文地址:https://www.cnblogs.com/ljxdtc666/p/12430136.html

时间: 2024-08-29 06:50:31

CF1321-World of Darkraft: Battle for Azathoth (线段树+二维偏序)的相关文章

HDU6638 Snowy Smile (线段树+二维最大子段和)

2019杭电多校第六场的一道签到题 这次我们显然要求的二维矩阵的最大值,分析题目我们可以得到几个细节. 1.首先数据很大,肯定要离散化. 2.离散化后,我们想象有很多点在一个平面内,要统计矩阵最大值 3.我们之前接触过如何求一条线上的最大子段和,只要用线段树维护四个值就能够解决 4.根据已知,我们发现求矩阵和也是可以这么做的,因为他是一个矩形,所以我们假如我们有两行,其实可以把第二行的对应数据加到第一行上去,进行压维操作,再求一维的最大子段和. 5.我们要考虑所有的情况,因此我们以x轴作为线段树

知识点 - 线段树 权值 树套树 二维 可持续

知识点 - 线段树 权值 树套树 二维 可持续 //区间更新求和 inline int ls(int p) { return p << 1; }//左儿子 inline int rs(int p) { return p << 1 | 1; }//右儿子 void push_up(int p) { t[p] = t[ls(p)] + t[rs(p)]; }// 向上不断维护区间操作 void build(ll p, ll l, ll r) { if (l == r) { t[p] =

Codeforces Round #625 Div1 C,二维偏序,排序+线段树

题目 题意: 有若干武器A,攻击力A1,费用A2, 有若干铠甲B,防御力B1,费用B2, 有若干怪兽M,攻击力M1,防御力M2,奖励M3 你可以选择一把武器,一个铠甲,打败所有攻击和防御都严格小的怪兽,问最大收益. 思路: 典型的二维偏序问题,把攻击和防御想象成二维的坐标轴,我们要找到的其实就是一个矩形里(0,0)~(x,y)里收益-这个矩形的花费的最大值.我们可以排序一维,另一维用线段树维护,枚举的时候往里面加怪兽就好. 代码: #include <bits/stdc++.h> using

CodeForces 1321E. World of Darkraft: Battle for Azathoth(线段树)

传送门 题意 给 \(a_1,a_2,...,a_n\),\(b_1,b_2,...,b_m\),\((x_1,y_1,w_1),(x_2,y_2,w_2),...,(x_p,y_p,w_p)\), 问 \[\max_{1\le i\le n,1\le j\le m}\{\sum_{x_k<a_i,y_k<b_j}w_k-a_i-b_j\}\] 其实就是分别选一个 \(a_i,b_j\),求矩形 \((0,0),(a_i,b_j)\) 内部的点的 \(w\) 总和减 \(a_i+b_j\) 的

POJ 2155 2维线段树 || 2维BIT

#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <vector> #include <map> #include <set> #include <stack> #define mp make_pair #define pa pair<int,int> #define pb p

线段树(多维+双成段更新) UVA 11992 Fast Matrix Operations

题目传送门 题意:训练指南P207 分析:因为矩阵不超过20行,所以可以建20条线段的线段树,支持两个区间更新以及区间查询. #include <bits/stdc++.h> using namespace std; #define lson l, mid, o << 1 #define rson mid + 1, r, o << 1 | 1 typedef long long ll; const int INF = 0x3f3f3f3f; const int N =

线段树&#183;二

1.UVA 11525 Permutation 题意:求1~k这k个数中第N个排列.(N从0开始记).N=sum(Si*(k-i)!)(1≤i≤k) 思路:根据N的值的性质,联系康拓展开,不妨发现第i位的值为剩下没用的数中从小到大第Si+1个.可以用线段树来记录区间内没有用的数的个数. 1 #include<iostream> 2 using namespace std; 3 int k; 4 const int maxk = 50010; 5 int numk[maxk]; 6 int tr

线段树(二)

第一篇以一道简单的题目为背景介绍了线段树的基本结构和基本性质,这一篇我们使用线段树来解决几个常见的问题 1. 查询区间最大(小)值 支持两种操作:a. 修改某个点的值 b. 查询某个区间的最大(小)值 1 #include <stdio.h> 2 #define N 1024 3 4 typedef struct { 5 int min_value; 6 int left; 7 int right; 8 } ST; 9 10 int input[N + 1]; 11 int father[N

线段树の二 区间乘+区间加

具体就不解释了,看上一篇文章 放代码 注意点:!!!! 注意运算符优先级 比如: a*=b%p 是b先mod p再与a相乘 参见:https://baike.baidu.com/item/%E8%BF%90%E7%AE%97%E7%AC%A6%E4%BC%98%E5%85%88%E7%BA%A7/4752611?fr=aladdin /******************************* 线段树V2.0 支持区间加.区间乘.区间和查询 *************************