gfoj 792 求和

Problem 792: 求和
Time Limit: 1000 ms Memory Limit: 262144 KB

Problem Description
给你一个数列A[1..n],长度为n。

令f(l,r,k)为A[l..r]里的第k大的元素。

特别的,当r-l+1

题解

看到这道题
我们不难想出O(n^2)的暴力
枚举每一个区间
求出他的第k大
怎么求第k大呢
暴力扫描?主席树?(都是sd)
用一个优先队列(小根堆)维护就行了

接着开始想优化
觉得可以二分找出比小根堆堆顶大的下标
发现不是单调
gg了

然后发现O(nk)是可以a的
然后可以想到用一个双向队列
每次从小到大删除(添加比较麻烦)
计算每个点的贡献(不是区间)
找到以x为最右边的区间(x是第k大)
右移到以x为最左边的区间

比较坑的地方在于pre【】数组
一开始想当然的以为r会沿着lc【r】回去
后来发现在n+1处有自环(自己走到自己)
这样返回时也要走自环
记录pre【i】=j表示
第i次右移是从j走来的
区别:
lc【i】:每个点对应一个点
pre【i】:每次移动对应一个点(每个点对应多个点)

暴力

#include<cstdio>
#include<iostream>
#include<queue>
#define N 200010
using namespace std;
typedef long long LL;
int n,a[N],m;
LL ans;
int main(){
    freopen("data.txt","r",stdin);
    freopen("2.txt","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++){
        priority_queue<int,vector<int>,greater<int> >Q;
        for(int j=0;j<m;j++)Q.push(a[j+i]);
        ans+=Q.top();
        for(int j=i+m;j<=n;j++){
            if(a[j]>Q.top()){
                Q.pop();
                Q.push(a[j]);
            }
            ans+=Q.top();
        }
    }
    cout<<ans<<'\n';
}

ac代码

#include<cstdio>
#include<iostream>
#define N 200010
using namespace std;
typedef long long LL;
int n,m,p,a[N],b[N],lc[N],rc[N],l,r,h,t,cnt,tot,mid,pre[N];
LL ans;
int main(){
    freopen("data.txt","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&p);
        b[p]=i;
        lc[i]=i-1;
        rc[i]=i+1;
    }
    rc[n+1]=n+1;
    for(int i=1;i<=n;i++){
//      printf("%d\n",i);
        l=r=mid=b[i];
        cnt=m;
        while(cnt--){
            pre[m-cnt]=r;
            r=rc[r];
        }
        cnt=m;
        while(cnt--){
            ans+=(LL)(l-lc[l])*(r-pre[cnt+1])*i;
//          printf("%d %d ",l,r);
//          cout<<ans<<'\n';
            l=lc[l];
            r=pre[cnt+1];
        }
        rc[lc[b[i]]]=rc[b[i]];
        lc[rc[b[i]]]=lc[b[i]];
    }
    cout<<ans<<'\n';
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值