Sequence(ST表)(洛谷P2048)
作者:互联网
知识储备
在做这道题前,我们先要了解一下ST表(一种离线求区间最值的方法)
ST表使用DP实现的,其查询复杂度为O(1).
那么我们怎么用DP实现呢??
首先,我们设立一个状态f[i][j],其中i代表起点(包括在内),而j则代表是2的几次方,那么这个状态的含义就是以i为起点,求i~i+2^j-1内的区间最大值
最开始,我们定义f[i][0]的值为其本身即a[i]
那么我们转移方程就是
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1)][j-1]); //一层一层的推过来...
然后我们就知道在一定区间内的最大值了!!
查询也是一个需要特别注意的点,我们要知道查询的x位置和y位置之间max,就要知道y-x+1是y的几次方,其中这个要向下取整,防止越界,但是这样我们就会有一部分扫不到,由于我们向下取整的这个区间是大于等于这个区间的一半的,所以我们可以反过来,从y-(1<<k)到y,这样可以保证不会漏掉数据,并且重复的话也不会对答案产生影响
但是注意!!算k的式子要小心啊
别取错了,一个小BUG就会导致你WA掉...
#include<bits/stdc++.h> #define ll long long #define FOR(i,a,b) for(register int i=a;i<=b;i++) #define ROF(i,a,b) for(register int i=a;i>=b;i--) using namespace std; const int N=500000+10; int n,m; int a[N]; int f[N][30]; int scan() { int as=0,f=1; char c=getchar(); while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9'){as=(as<<3)+(as<<1)+c-'0';c=getchar();} return as*f; } void RMQ(int n) { FOR(i,1,20) { FOR(j,1,n) { if(j+(1<<i)-1<=n) f[j][i]=max(f[j][i-1],f[j+(1<<(i-1))][i-1]); } } } int main() { n=scan(); m=scan(); FOR(i,1,n) a[i]=scan(),f[i][0]=a[i]; RMQ(n); FOR(i,1,m) { int x,y; x=scan(); y=scan(); int k=(int)(log((double)(y-x+1))/log(2.0)); printf("%d\n",max(f[x][k],f[y-(1<<k)+1][k])); } return 0; }代码戳这里
接下来我们要讲正事了!!
我们的目光转到钢琴这一题上
思路
首先,我们前缀和储存值,然后在用KMQ,将一些区间内的max求出来
然后我们根据题目要求,由于题目要我们求最大权值,那么我们就求每个可能区间里面的max,将其储存在堆里面.
但是最开始的时候,我们只求出了以某一点为起点的最大值,但是我们最后要求的1~k个最大值的相加
所以我们在每取出一个max是,再去寻找以该点为起点的max,只不过此时的max不是当前的最有解,而是次优解,但是也可能成为新的最有解,
然后题目打这里就差不多啦
#include<bits/stdc++.h> #define ll long long #define gc getchar() #define FOR(i,a,b) for(register int i=a;i<=b;i++) #define ROF(i,a,b) for(register int i=a;i>=b;i--) using namespace std; const int N=500000+10; int n,m,L,R; ll sum[N]; int id[N][30],f[N][30]; ll ans=0; int scan() { int as=0,f=1;char c=gc; while(c>'9'||c<'0'){if(c=='-') f=-1;c=gc;} while(c>='0'&&c<='9'){as=(as<<3)+(as<<1)+c-'0';c=gc;} return as*f; } //learn new struct s1 { int u,l,r,pos; bool operator <(s1 y) const { return sum[pos]-sum[u-1]<sum[y.pos]-sum[y.u-1];//大的放队尾 } }tmp; priority_queue<s1>q;//队列 void init(int &pos,int l,int r) { int j=log2(r-l+1); if(f[l][j]>f[r-(1<<j)+1][j]) pos=id[l][j]; else pos=id[r-(1<<j)+1][j]; }//这里都懂吧,KMQ的查询 int main() { n=scan();m=scan();L=scan();R=scan(); FOR(i,1,n) { sum[i]=scan();sum[i]+=sum[i-1];//前缀和 f[i][0]=sum[i]; id[i][0]=i;//位置的处理 } FOR(j,1,20) { FOR(i,1,n) { if(i+(1<<j)-1>n) break; if(f[i][j-1]>f[i+(1<<(j-1))][j-1]) f[i][j]=f[i][j-1],id[i][j]=id[i][j-1]; else f[i][j]=f[i+(1<<(j-1))][j-1],id[i][j]=id[i+(1<<(j-1))][j-1]; }//这里其实就是KMQQAQ } FOR(i,1,n-L+1)//入坑silasila...... { int l=i+L-1; int r=min(i+R-1,n),pos=0,u=i;//随便给pos一个值 init(pos,l,r);//pos在这里赋好了值 tmp=(s1){u,l,r,pos};//然后加入四元组吧.. q.push(tmp);//入队 } while(m--) { tmp=q.top(); q.pop();//弹出顶端,即最优解 int u=tmp.u,l=tmp.l,r=tmp.r,tps=tmp.pos,pos=tmp.pos;//去寻找次 //优解 ans+=sum[tps]-sum[u-1]; if(tps>l) { init(pos,l,tps-1);tmp=(s1){u,l,tps-1,pos};q.push(tmp); } if(tps<r) { init(pos,tps+1,r);tmp=(s1){u,tps+1,r,pos};q.push(tmp); } } printf("%lld",ans); return 0; }代码戳这里
标签:洛谷,int,max,ll,long,ST,define,我们,P2048 来源: https://www.cnblogs.com/KSTT/p/10380545.html