题意:分别给出两个序列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;
}