蒟蒻不会单调队列,怎么办?另类方法,一样AC
题目要我们求最小的,这看上去就不太好直接求。怎么办?
一个常见的想法,将原问题转换成判定性问题:给定弹跳距离的范围,能否获得至少分。(注意:玩家可以在任意时刻结束游戏,所以只要出现一个时刻的分数
满足
即可)
一个朴素的想法:可以DP!
每次找前面合法状态(即符合弹跳距离)中当前分数最大的状态转移。
,朴素的枚举好像会挂。怎么优化?
注意到每次事实上有用的只有分数最大的那个状态,想到了啥?
RMQ?优先队列?(我也不知道为啥想到的不是单调队列而是优先队列)
想到这里那么这一题已经基本做完了。
用优先队列维护所有合法状态,每次取出堆顶转移即可。
实测不开O2可以AC
也许我的做法比较平易近人
/*
Author:Frozencode
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 500010;
const ll INF = 2147483647;
struct node{
ll pos,pts;
}a[maxn];//pts为该点得分。
struct tem{
ll pos,sum;
bool operator < (const tem &a)const//结构体优先队列必备之重载运算符。
{
return sum < a.sum;
}
};//sum为当前的分数。
priority_queue<tem>q;
priority_queue<tem>iq;
tem cur;
ll n,d,k,l,r,res;
void init(ll i,ll x){
while(!q.empty()){
cur = q.top();
if(a[i].pos - cur.pos > d + x){q.pop();}//剔除不合法状态
else if(a[i].pos - cur.pos < max(1ll * 1,d - x)){
iq.push(q.top());
q.pop();
}//对于当前状态此状态不合法,但是以后的状态可能会用到,得先留着。
else break;//发现最大值,转移走起。
}
return;
}
bool check(ll x){
while(!q.empty())q.pop();
while(!iq.empty())iq.pop();
cur.pos = 0;
cur.sum = 0;
q.push(cur);//以上为初始化。
for(int i = 1;i <= n;i++){
init(i,x);
if(q.empty())
{
while(!iq.empty()){
q.push(iq.top());
iq.pop();
}
continue;//没法转移,此状态拜拜~ 还原现场。
}
cur.pos = a[i].pos;
cur.sum = cur.sum + a[i].pts;
if(cur.sum >= k)return true;//符合条件 成功!
q.push(cur);//添加新状态。
while(!iq.empty()){
q.push(iq.top());
iq.pop();
}还原现场。
}
return false;//失败 T_T
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>d>>k;
for(int i = 1;i <= n;i++){
cin>>a[i].pos>>a[i].pts;
r = max(r,a[i].pos);
}
l = res = -1;//如果答案一次都没更新到说明无论如何都没法达到k分。输出-1。
++r;
while(l < r - 1){
ll mid = (l + r)>>1;
if(check(mid)){
r = mid;
res = r;//更新答案。
}
else l = mid;
}//二分没啥好说的。
cout<<res;
return 0;
} 后话:这种做法其实是可以被卡到的,所以只有在实在想不到别的做法的时候可以一试。
你以为这就结束了?NO!
作为一道单调队列板子题,怎么能不用单调队列做呢(雾
由上面的分析可得,用deque维护所有合法状态,可以解决这个问题。
/*
Author:Frozencode
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 500010;
const ll INF = 2147483647;
struct node{
ll pos,pts;
}a[maxn];
deque<ll> q;
ll n,d,k,l,r,res,dp[maxn];
bool check(ll x){
memset(dp,128,sizeof(dp));
while(!q.empty())q.pop_front();
dp[0] = 0;
ll cur = 0;
for(int i = 1;i <= n;i++)
{
while(cur < i && a[i].pos - a[cur].pos >= max(1ll * 1,d - x)){//合法状态进队。
while(!q.empty() && dp[q.back()] <= dp[cur])q.pop_back();//比当前状态劣就弹出。
q.push_back(cur);
cur++;
}
while(!q.empty() && a[i].pos - a[q.front()].pos > d + x)q.pop_front();//不合法状态出队。
if(!q.empty())dp[i] = dp[q.front()] + a[i].pts;
if(dp[i] >= k)return true;
}
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>d>>k;
for(int i = 1;i <= n;i++){
cin>>a[i].pos>>a[i].pts;
r = max(r,a[i].pos);
}
l = res = -1;
++r;
while(l < r - 1){
ll mid = (l + r)>>1;
if(check(mid)){
r = mid;
res = r;
}
else l = mid;
}
cout<<res;
return 0;
}


京公网安备 11010502036488号