BZOJ2006 ST表 + 堆

https://www.lydsy.com/JudgeOnline/problem.php?id=2006

题意:在长度N的序列中求K段长度在L到R之间的区间,使得他们的和最大

很容易想到要求一个前缀和。

然后每一个位置i就对应后面的一段i + L - 1 ~ i + R - 1的区间,如果考虑暴力的话,就把每一个值对应的区间内所有的值再全部加入优先队列,取出K个。

看了下数据范围发现不可行。

考虑ST表求最值,在每一个对应区间内找一个最大值,仔细一想也觉得不可行,因为一个区间内的次大值很有可能是会对答案产生贡献的。

事实上我们对ST表加入一点小操作,使得他维护的是静态区间内的最大值下标,rmq(l,r)返回的是l到r区间内最大值的下标p,我们首先将N个三元组i,l,r加入堆,他的贡献是pre[rmq(l,r)] - pre[i - 1],将贡献从大到小排序,当我们处理完一个三元组的时候,再加入分裂开的三元组i,l,p - 1与i,p + 1,r,也就是说,当区间内最大值计算完之后,就往堆中放入一个次大值和次次大值,以此类推即可。

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)  
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))  
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);  
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);  
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long  
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second 
typedef vector<int> VI;
int read(){int x = 0,f = 1;char c = getchar();while (c<'0' || c>'9'){if (c == '-') f = -1;c = getchar();}
while (c >= '0'&&c <= '9'){x = x * 10 + c - '0';c = getchar();}return x*f;}
const double eps = 1e-9;
const int maxn = 5e5 + 10;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7; 
int N,M,L,R;
int a[maxn];
LL pre[maxn];
const int SP = 20;
int Max[maxn][SP];
int mm[maxn];
void initRMQ(int n,LL b[]){
    mm[0] = -1;
    for(int i = 1; i <= n; i ++){
        mm[i] = ((i & (i - 1)) == 0)?mm[i - 1] + 1:mm[i - 1];
        Max[i][0] = i;
    }
    for(int j = 1; j <= mm[n]; j ++){
        for(int i = 1; i + (1 << j) - 1 <= n ; i ++){
            int x = Max[i][j - 1],y = Max[i + (1 << (j - 1))][j - 1];
            Max[i][j] = b[x] > b[y]?x:y;
        }
    }
}
int rmq(int x,int y,LL b[]){
    int k = mm[y - x + 1];
    return b[Max[x][k]] > b[Max[y - (1 << k) + 1][k]]?Max[x][k]:Max[y - (1 << k) + 1][k];
}
struct node{
    int l,r,o;
    node(){}
    node(int l,int r,int o):l(l),r(r),o(o){}
    LL ans(){
        return pre[rmq(l,r,pre)] - pre[o - 1];
    }
    friend bool operator < (node a,node b){
        return a.ans() < b.ans();
    }
};
int main(){
    N = read(); M = read(); L = read(); R = read();
    for(int i = 1; i <= N ; i ++){
         a[i] = read();
         pre[i] = pre[i - 1] + a[i];
    }
    initRMQ(N,pre);
    priority_queue<node>Q;
    for(int i = 1; i <= N ; i ++){
        if(i + L - 1 > N) break;
        Q.push(node(i + L - 1,min(i + R - 1,N),i));
    }
    LL ans = 0;
    while(M--){
        node u = Q.top(); Q.pop();
        ans += u.ans();
        int p = rmq(u.l,u.r,pre);
        if(p != u.l) Q.push(node(u.l,p - 1,u.o));
        if(p != u.r) Q.push(node(p + 1,u.r,u.o));
    }
    Prl(ans);
    return 0;
}

 

转载于:https://www.cnblogs.com/Hugh-Locke/p/10364503.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值