这里主要阐述一下我的分析思路。
看起来挺直观的。
最初的想法,我们枚举每一个最远点mxp的位置,然后对之前的a进行排序。
那么以mxp为最远点,选x个的最大疲劳值为:
2∗s[mxp]+a[mxp]+(之前的前x−1大的a值的和)
这样的复杂度为 O(n2logn) ,考试时就这样拿了个60pt。
但是,我们要尝试发现这道题的特性,来进行时间上的优化。
根据极大化思想,我们要尽可能排除不影响答案的 (mxp,x) 。
当 x 一定时,设 i<j , i 没有 j 优,这等价于:
2∗s[i]+a[i]+(i之前的前x−1大的a值的和)<2∗s[j]+a[j]+(j之前的前x−1大的a值的和)
记 w[i]=2∗s[i]+a[i]
∴w[i]−w[j]<(j之前的前x−1大的a值的和)−(i之前的前x−1大的a值的和)
当 x 增大的时候,例如 x 变大到 x+1 ,发现 (j之前的前x大的a值的和)−(i之前的前x大的a值的和) 的值一定是递增的,因为 j之前的前x大的a值的和 一定是 j之前的前x−−1大的a值的和 多一个数, i 也一样,而 i 能选择到的 j 也能选择得到。
所以我们得到了决策单调性:对于 x , i<j , i 没有 j 优,那么随着 x 的增大, i 仍然没有 j 优,所以对于 x 的询问的决策点会非严格单调递增。
接下来,很容易想到用单调队列什么的进行维护。
但怎么尝试都觉得不行……
这时候就一定要跳出来啦。
根据决策单调性这个重要的特点,考虑换一种思考的角度。
假如当前 x−1 这个询问我们决策点为 cur ,答案为 res ,现在要求 x 这个询问的决策点和答案。
我们有两种方法:
①在 cur 之前选择一个没有选择过的点 i , Δ=a[i]
②在 cur 之后选择一个决策点, Δ=2∗s[i]+a[i]−2∗s[cur]
用两个堆实现即可。
有点意思。
#include
#include
#include
using namespace std;
#define rep(i,a,b) for (int i=(a);i<=(b);i++)
#define x first
#define y second
#define mp make_pair
typedef pair<int,int> PII;
const int N=131072;
int n;
int s[N],a[N];
int cur,vis[N];
priority_queue qs,qb;
int res;
inline int rd(void)
{
int x=0,f=1; char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
int main(void)
{
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
n=rd();
rep(i,1,n) s[i]=rd();
rep(i,1,n) a[i]=rd();
rep(i,1,n)
qb.push(mp(2*s[i]+a[i],i));
PII t1,t2; int e1,e2,cs;
rep(i,1,n)
{
while (!qs.empty())
{
t1=qs.top();
if (vis[t1.y])
qs.pop();
else break;
}
while (!qb.empty())
{
t2=qb.top();
if (t2.yy])
qb.pop();
else break;
}
e1=(!qs.empty());
e2=(!qb.empty());
if (!e1&&e2)
cs=2;
else if (e1&&!e2)
cs=1;
else if (e1&&e2)
{
t1=qs.top(),t2=qb.top();
if (t2.x-2*s[cur]>=t1.x)
cs=2;
else cs=1;
}
if (cs==1)
{
t1=qs.top(); qs.pop();
vis[t1.y]=1;
res+=t1.x;
}
else if (cs==2)
{
t2=qb.top(); qb.pop();
vis[t2.y]=1;
rep(j,cur+1,t2.y)
if (!vis[j])
qs.push(mp(a[j],j));
res+=(t2.x-2*s[cur]);
cur=t2.y;
}
printf("%d\n",res);
}
return 0;
}