(2017多校2)1003/hdu-6047 Maximum Sequence(单调队列/优先队列)

点我看题

题意:分别给出两个序列a和b,根据规则 ai ≤max{ aj -j│ bk ≤j<i}(其中n<i<=2*n),求得一个序列an+1-a2n,要求其总和最大.

官方题解:预处理:a_i -= i ,易证明从最小的b开始选每次选最大的一定可以使结果最大。 证明思路:如果条件改为a_i<=max{a_j-j|b_k<=j<=n},那么b的顺序与最后的结果无关。条件改回来后,由于每次要计算一个数的最大值时都有a_(n+1)...a_(i-1)在范围中,所以每次只需让a_i - i尽可能大,那么就把大的数尽早用上,每次一定考虑尽量多的数字,这样取得的数字就尽可能的大。 所以说每次就是求区间最值,加在答案上。由于贪心的思路,每次要求的区间的下界是单调不降的,故可以用单调队列优化到O(n)的复杂度。 由于1 ≤ b_i ≤ n,对b排序可以用哈希排序(桶排序)完成。

进一步观察,可以发现这样贪心时 a_(n+1)...a_i 其实是单调不增的,所以并不需要每次求区间最值了,选第一个数时就选最大的,后面的选择顺序与最终结果无关了

分析:Contest的时候用优先队列写的,当时比较担心超时,后来想想应该也不会超时,因为进队出队的过程都是O(log2n),不过后来发现用单调队列更节约时间.


参考代码:

//优先队列

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn = 250010;
const int mod = 1e9+7;
int n;
int a[maxn];
int b[maxn];
struct Node{
    int val;
    int id;
    friend bool operator < ( const Node &a, const Node &b)
    {
        if( a.val != b.val)
            return a.val < b.val;
        return a.id > b.id;
    }
};
priority_queue<Node> q;

int main()
{
    while( ~scanf("%d",&n))
    {
        while( !q.empty())
            q.pop();
        Node tmp;
        for( int i = 1; i <= n; i++)
        {
            scanf("%d",&a[i]);
            tmp.id = i;
            tmp.val = a[i]-i;
            q.push(tmp);
        }
        for( int i = 1; i <= n; i++)
            scanf("%d",&b[i]);
        sort(b+1,b+1+n);
        ll ans = 0;
        int p = 1;
        while( p <= n)
        {
            tmp = q.top();
            while( b[p] > tmp.id)
            {
                q.pop();
                tmp = q.top();
            }
            ans=(ans+tmp.val)%mod;
            tmp.val -= (n+p);
            tmp.id = n+p;
            q.push(tmp);
            p++;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

//单调队列

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>

using namespace std;
#define LL long long
const int mod = 1e9+7;
const int maxn = 250010;
int n;
int a[maxn];
int b[maxn];
struct Node{
    int id;
    int val;
};
Node q[maxn<<1];

int main()
{
    while( ~scanf("%d",&n))
    {
        int head = 0;
        int tail = 0;
        for( int i = 1; i <= n; i++)
        {
            scanf("%d",&a[i]);
            if( tail == 0)
            {
                q[tail].id = i;
                q[tail++].val = a[i]-i;
            }
            else
            {
                while( head < tail && a[i]-i >= q[tail-1].val)
                    tail--;
                q[tail].id = i;
                q[tail++].val = a[i]-i;
            }
        }
        for( int i = 1; i <= n; i++)
            scanf("%d",&b[i]);
        sort(b+1,b+1+n);

        int p = 1;
        Node tmp;
        LL ans = 0;
        while( p <= n)
        {
            while( b[p] > q[head].id)
                head++;
            ans = (ans+q[head].val)%mod;
            tmp.id = p+n;
            tmp.val = q[head].val-(n+p);
            while( head < tail && tmp.val >= q[tail-1].val)
                tail--;
            q[tail++] = tmp;
            p++;
        }
        printf("%lld\n",ans);

    }

    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值