题解 洛谷P2048 [NOI2010]超级钢琴

RMQ好题

这是RMQ的一道难题了吧,至少作为蒟蒻的我是这么认为的

开始我的表演~~

自认为踩遍所有坑

首先给出我题解中的变量定义

k: 需要超级和弦总数

l,r: 分别是超级和弦所包含音符个数的下限和上限

dp[i][j]: RMQ数组,表示i~(i+2^j-1)最大值

num[i][j]: dp[i][j]中最大值对应的位置(结束点)


第一部分 区间最大美妙度超级和弦的解决

首先,因为我们需要一段每妙度的和,我们可以用一个前缀和数组c[i]

若我们找到了一个区间 L~R

那么我们如何在这一段区间中找到最大的美妙度和呢

扫描二维码关注公众号,回复: 10513878 查看本文章

显然,如上图所示,最大的美妙度之和应为 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;
}

完结撒花~~

管理大大求通过

发布了28 篇原创文章 · 获赞 8 · 访问量 606

猜你喜欢

转载自blog.csdn.net/zty_ju/article/details/96474214
今日推荐