RMQ好题
这是RMQ的一道难题了吧,至少作为蒟蒻的我是这么认为的
开始我的表演~~
自认为踩遍所有坑
首先给出我题解中的变量定义
k: 需要超级和弦总数
l,r: 分别是超级和弦所包含音符个数的下限和上限
dp[i][j]: RMQ数组,表示i~(i+2^j-1)最大值
num[i][j]: dp[i][j]中最大值对应的位置(结束点)
第一部分 区间最大美妙度超级和弦的解决
首先,因为我们需要一段每妙度的和,我们可以用一个前缀和数组c[i]
若我们找到了一个区间 L~R
那么我们如何在这一段区间中找到最大的美妙度和呢
显然,如上图所示,最大的美妙度之和应为 max(c[i])-c[L-1]
这道题中应是这样的
那么这个问题就转化成了找出前缀和数组 c[i] 的最大值
RMQ登场了
about RMQ
第二部分 RMQ
这道题根据上面的分析我们应去找c[i]的最大值
才能去解决最大美妙度的问题
附RMQ模板
void RMQ(){
for(int j=1;j<=maxlog;j++){
for(int i=1;i<=n;i++){
if(i+(1<<j)-1<=n)
dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
}
这道题RMQ的不同之处在哪呢?
在于我们需要知道我们取的最大值的位置
不然我们会不知道我们究竟取了哪一段音符,有可能会取重或者取不到
如何在RMQ中找到最大值位置呢?
其实不难,在dp[i][j]取数时哪个大num[i][j]就取谁
void RMQ(){
for(int j=1;j<=20;j++){
for(int i=1;i<=n;i++){
if(i+(1<<j)-1<=n){
if(dp[i][j-1]<dp[i+(1<<(j-1))][j-1]){
dp[i][j]=dp[i+(1<<(j-1))][j-1];
num[i][j]=num[i+(1<<(j-1))][j-1];
}
else{
dp[i][j]=dp[i][j-1];
num[i][j]=num[i][j-1];
}
}
}
}
}
第三部分 k个最大超级和弦的解决
那怎么去解决k个超级和弦呢?
我们可以用优先队列来解决这个问题
首先我们可以将以1n为开头的长度为lr的超级和弦中
分别是最大的n个超级和弦放进队列
按值从大到小排序
此时我们取出队列中的队顶,这一定是全局最大的美妙度
接下来如何找到第二大的呢?
-
我们可以破开最大的这一个
-
用 w 表示当前最大值结束位置(num[i][])
-
将原本的 i~w 这个区域pop掉
-
将i~ ((i+l-1)—(w-1)),i~((w+1)—(i+r-1))中的最大值push进去
-
再取队顶出来重复刚才操作
为何这样操作能找到最大美妙度呢
第四部分 细节处理与代码
这样我们的题目就基本上解决了
开心开心~~
听取WA声一片啊
附上我的10分代码:
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
const int maxn=500005;
struct node{
int data;
int s,w;
bool operator <(node i)const{
return data<i.data;
}
};
int c[maxn],dp[maxn][20],lg[maxn];
int num[maxn][20];
bool vis[500000000];
int p[maxn];
int n,k,l,r;
int ans=0;
int maxnum=0x3f3f3f3f;
priority_queue<node>q;
void RMQ(){
for(int j=1;j<=20;j++){
for(int i=1;i<=n;i++){
if(i+(1<<j)-1<=n){
if(dp[i][j-1]<dp[i+(1<<(j-1))][j-1]){
dp[i][j]=dp[i+(1<<(j-1))][j-1];
num[i][j]=num[i+(1<<(j-1))][j-1];
}
else{
dp[i][j]=dp[i][j-1];
num[i][j]=num[i][j-1];
}
}
}
}
}
void work(int i,int x,int y){
int tmp=lg[y-x+1];
int o,jl;
bool flag=0;
if(dp[x][tmp]>dp[y-(1<<tmp)+1][tmp]){
o=dp[x][tmp];
o-=c[i-1];
jl=num[x][tmp];
}
else{
o=dp[y-(1<<tmp)+1][tmp];
o-=c[i-1];
jl=num[y-(1<<tmp)+1][tmp];
}
if(vis[o]==1)flag=1;
vis[o]=1;
//cout<<i<<" "<<o-c[i-1]<<" "<<jl<<" "<<endl;
if(!flag){
node u;
u.data=o;
u.s=i;
u.w=jl;
q.push(u);
}
}
int main()
{
cin>>n>>k>>l>>r;
lg[0]=-1;
for(int i=1;i<=n;i++){
int x;
cin>>x;
c[i]=c[i-1]+x;
dp[i][0]=c[i];
num[i][0]=i;
lg[i]=lg[i>>1]+1;
}
RMQ();
for(int i=1;i+l-1<=n;i++)
work(i,i+l-1,min(i+r-1,n));
ans+=q.top().data;
//cout<<"#"<<q.top().data<<endl;
int d=q.top().w,f=q.top().s;
for(int i=1;i<k;i++){
q.pop();
if(d-1>=f+l-1&&d-1>=1)
work(f,f+l-1,d-1);
if(d+1<=f+r-1&&d+1<=n)
work(f,d+1,min(f+r-1,n));
d=q.top().w;
f=q.top().s;
ans+=q.top().data;
//cout<<"#"<<q.top().data<<endl;
}
cout<<ans;
return 0;
}
究竟是哪里有问题呢?
是因为我的10分代码无法解决这种情况:
详情见下
正确代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=500005;
struct node{
***
int num;
int lid,rid;
int w;
int data;
bool operator <(node i)const{
return data<i.data;
}
***
};
int c[maxn],dp[maxn][20],lg[maxn];
int num[maxn][20];
int p[maxn];
int n,k,l,r;
int minn=2147483647;
long long ans=0;
priority_queue<node>q;
void RMQ(){
for(int j=1;j<=20;j++){
for(int i=1;i<=n;i++){
if(i+(1<<j)-1<=n){
if(dp[i][j-1]<dp[i+(1<<(j-1))][j-1]){
dp[i][j]=dp[i+(1<<(j-1))][j-1];
num[i][j]=num[i+(1<<(j-1))][j-1];
}
else{
dp[i][j]=dp[i][j-1];
num[i][j]=num[i][j-1];
}
}
}
}
}
int wz(int x,int y){
int tmp=lg[y-x+1];
int o,jl;
if(dp[x][tmp]>dp[y-(1<<tmp)+1][tmp])
jl=num[x][tmp];
else
jl=num[y-(1<<tmp)+1][tmp];
return jl;//找到位置
}
void add(int po,int i,int left,int right){
node tmp;
tmp.w=po;
tmp.num=i;
tmp.lid=left;
tmp.rid=right;
tmp.data=c[po]-c[i-1];
q.push(tmp);
return ;
}
int main()
{
cin>>n>>k>>l>>r;
lg[0]=-1;
for(int i=1;i<=n;i++){
int x;
cin>>x;
c[i]=c[i-1]+x;
dp[i][0]=c[i];
num[i][0]=i;
lg[i]=lg[i/2]+1;
}
RMQ();
***
for(int i=1;i+l-1<=n;i++)
add(wz(i+l-1,min(i+r-1,n)),i,i+l-1,min(i+r-1,n));
***
while(k--){
int d=q.top().w,left=q.top().lid,right=q.top().rid;
int e=q.top().data,id=q.top().num;
ans+=e;
q.pop();
***
if(d>left)
add(wz(left,d-1),id,left,d-1);
if(d<right)
add(wz(d+1,right),id,d+1,right);
***
}
cout<<ans<<endl;
return 0;
}
完结撒花~~
管理大大求通过