ST表
好久没写了, 所以写篇博客来回忆一下
用于解决RMQ问题
主要运用倍增的思想
具体做法:
1. 设f[i][j]为 $ [i,i+2^j] $ 区间内的极值, 然后预处理
2. 查询时: 设查询区间长度为len,返回左端点为起点长度为$ 2^{log_2(len)} $ 与右端点为终点同等长度的二者中的极值
(此处log需要下取整)
void prepare()
{
lg[0]=-1;
for(re int i=1;i<=n;++i) lg[i]=lg[i/2]+1;
for(re int i=1;i<=lg[n];++i)
for(re int j=1;j<=n;++j)
f[j][i]=MAX(f[j][i-1], f[j+(1<<i-1)][i-1]);
}
int query(int l,int r)
{
int LOG=lg[r-l+1];
return MAX(f[l][LOG],f[r-(1<<LOG)+1][LOG]);
}
NOI2010 超级钢琴
题意简述:
对于一个长为n的序列求k段不同的长度在[L,R]中的区间, 使得选中的序列和的总和最大
n,m<=500000
Sol
常用小技巧: 碰到区间权值问题,可以考虑前缀和,转化为点权,然后运用数据结构即可
此题也是如此, 先转化为对于每个点,考虑其作为起点,则需要在之后[L,R]的区间中找到最大值
将所有最大值放入堆中,每弹出一次,就将该区间拆开
code
#include<bits/stdc++.h>
using namespace std;
#define re register
#define in inline
#define get getchar()
#define ll long long
in int read()
{
int t=0, x=1; char ch=get;
while(ch!='-' && (ch<'0' || ch>'9') ) ch=get;
if(ch=='-') ch=get, x=-1;
while(ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
return t*x;
}
const int _=1e6+23;
const int LG=22;
int f[_][LG],n,k,L,R,a[_],lg[_];
in int MAX(int x,int y){ return a[x]>a[y] ? x : y;}
in int query(int l,int r)
{
int LOG=lg[r-l+1];
return MAX(f[l][LOG],f[r-(1<<LOG)+1][LOG]);
}
struct yzx{
int l,r,num;
int val(){ return a[query(l,r)] - a[num-1];}
}; //表示每个可能最大值区间右端点范围[l,r],以及左端点编号num
yzx YZX(int a,int b,int c){return yzx{a,b,c}; }
bool operator < (yzx x,yzx y){ return x.val() < y.val(); }
priority_queue<yzx> q;
int main()
{
n=read(), k=read(), L=read(), R=read();
lg[0]=-1;
for(re int i=1;i<=n;++i) a[i]=read()+a[i-1], f[i][0]=i, lg[i]=lg[i/2]+1;
for(re int i=1;i<=lg[n];++i)
for(re int j=1;j<=n;++j)
f[j][i]=MAX(f[j][i-1], f[j+(1<<i-1)][i-1]);
for(re int i=1;i<=n;++i) {
int st=i+L-1;
if(st>n) break;
q.push(YZX(st,min(n,st+(R-L)),i));
}
ll ans=0;
while(k--)
{
yzx u=q.top(); q.pop();
ans+=u.val();
int pos=query(u.l,u.r);
if(pos>u.l) q.push(YZX(u.l,pos-1,u.num));
if(pos<u.r) q.push(YZX(pos+1,u.r,u.num));
}
cout<<ans<<endl;
return 0;
}