蒟蒻不会单调队列,怎么办?另类方法,一样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; }