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

2019杭电多校第六场的一道签到题

这次我们显然要求的二维矩阵的最大值,分析题目我们可以得到几个细节。

1.首先数据很大,肯定要离散化。

2.离散化后,我们想象有很多点在一个平面内,要统计矩阵最大值

3.我们之前接触过如何求一条线上的最大子段和,只要用线段树维护四个值就能够解决

4.根据已知,我们发现求矩阵和也是可以这么做的,因为他是一个矩形,所以我们假如我们有两行,其实可以把第二行的对应数据加到第一行上去,进行压维操作,再求一维的最大子段和。

5.我们要考虑所有的情况,因此我们以x轴作为线段树的区间,之后来枚举y轴,就可以把所有情况枚举出来。

6.这题爆int,并且我们可以不框住任何矩形,所以最小值肯定是0

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
typedef long long ll;
const int N=1e5+10;
const int inf=0x3f3f3f3f;
vector<int> xi;
vector<int> yi;
struct node{
    int l,r;
    ll sum;
    ll tsum;
    ll lsum;
    ll rsum;
}tr[N<<2];
struct qi{
    int x,y;
    ll w;
}q[N];
bool cmp(qi a,qi b){
    if(a.y==b.y)
    return a.x<b.x;
    return a.y<b.y;
}
void build(int u,int l,int r){
    if(l==r){
        tr[u]={l,l};
    }
    else{
        tr[u]={l,r};
        int mid=l+r>>1;
        build(u<<1,l,mid);
        build(u<<1|1,mid+1,r);
    }
}
int findy(int x){
    return lower_bound(yi.begin(),yi.end(),x)-yi.begin()+1;
}
int findx(int x){
    return lower_bound(xi.begin(),xi.end(),x)-xi.begin()+1;
}
void pushup(int u){
    tr[u].lsum=max(tr[u<<1].lsum,tr[u<<1].sum+tr[u<<1|1].lsum);
    tr[u].rsum=max(tr[u<<1|1].rsum,tr[u<<1|1].sum+tr[u<<1].rsum);
    tr[u].tsum=max(max(tr[u<<1].tsum,tr[u<<1|1].tsum),tr[u<<1].rsum+tr[u<<1|1].lsum);
    tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}
void modify(int u,int w,ll x){
    if(tr[u].l==tr[u].r){
        tr[u].lsum=tr[u].sum=tr[u].rsum=tr[u].tsum=tr[u].sum+x;
        return ;
    }
    else{
        int mid=tr[u].l+tr[u].r>>1;
        if(w<=mid)
        modify(u<<1,w,x);
        else
        modify(u<<1|1,w,x);
        pushup(u);
    }
}
int main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        int i;
        cin>>n;
        xi.clear();
        yi.clear();
        for(i=1;i<=n;i++){
            scanf("%d%d%lld",&q[i].x,&q[i].y,&q[i].w);
            xi.push_back(q[i].x);
            yi.push_back(q[i].y);
        }
        sort(q+1,q+n+1,cmp);
        sort(xi.begin(),xi.end());
        xi.erase(unique(xi.begin(),xi.end()),xi.end());
        sort(yi.begin(),yi.end());
        yi.erase(unique(yi.begin(),yi.end()),yi.end());
        int nx=xi.size();
        int ny=yi.size();
        int now=1;
        int k;
        ll ans=0;
        for(i=1;i<=ny;i++){  //枚举下边界
            build(1,1,nx);
            for(int j=i,k=now;j<=ny;j++){ //上边界                 

                while(k<=n&&findy(q[k].y)==j){
                    modify(1,findx(q[k].x),q[k].w);
                    k++;
                }
                if(j==i)
                now=k;
                ans=max(ans,tr[1].tsum);

            }
        }
        cout<<ans<<endl;
    }
}

原文地址:https://www.cnblogs.com/ctyakwf/p/12310912.html

时间: 2024-11-06 03:38:31

HDU6638 Snowy Smile (线段树+二维最大子段和)的相关文章

2019杭电多校第六场hdu6638 Snowy Smile(线段树+枚举)

Snowy Smile 题目传送门 解题思路 先把y离散化,然后把点按照x的大小进行排序,我们枚举每一种x作为上边界,然后再枚举其对应的每一种下边界.按照这种顺序插入点,这是一个压维的操作,即在线段树中的y位置加上其w,并利用线段树来更新动态的最大子段和. 代码如下 #include <bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; typedef long long ll; const int N = 2005; stru

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

题意: 题目大致意思是给你n把武器,m件防具,p个怪兽,接下来n行每行告诉你该武器的攻击力和花费, 接下来m行告诉你该防具的防御力和花费,然后p行每行告诉你这个怪兽的攻击力,防御力以及打败这个 怪兽可以获得的金钱数,当你的攻击力大于怪兽的防御力,并且你的防御力大于怪兽的攻击力时,你可 以打败这个怪兽.你必须至少购买1件武器和1件防具,问你最多可以获得多少钱. 链接:https://codeforces.com/contest/1321/problem/E 思路: 看了大神的题解,第一次知道二维偏

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

知识点 - 线段树 权值 树套树 二维 可持续 //区间更新求和 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] =

线段树维护区间最大子段和 枚举 HDU6638

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6638 题意:在一个二维坐标系上给你n(n<=2000)个点,点带有一个价值w(有正有负),点的坐标都在(-1e9,1e9)的范围之间,可任意用一个平行于坐标轴的矩形框住一片区域,求这片区域框住的点的价值和 分析:点的坐标范围太大,离散化应能想到.离散化后可以考虑枚举左边界,枚举左边界后按照横坐标的依次加点(以一列一列为单位),用线段树维护一列的最大子段和,每移动到新的一列,继续加点时,等价于向原先的

loj #535. 「LibreOJ Round #6」花火 树状数组求逆序对+主席树二维数点+整体二分

$ \color{#0066ff}{ 题目描述 }$ 「Hanabi, hanabi--」 一听说祭典上没有烟火,Karen 一脸沮丧. 「有的哦-- 虽然比不上大型烟花就是了.」 还好 Shinobu 早有准备,Alice.Ayaya.Karen.Shinobu.Yoko 五人又能继续愉快地玩耍啦! 「噢--!不是有放上天的烟花嘛!」Karen 兴奋地喊道. 「啊等等--」Yoko 惊呼.Karen 手持点燃引信的烟花,「嗯??」 Yoko 最希望见到的是排列优美的烟火,当然不会放过这个机会-

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