Jump and Treasure

传送门
题意:
Tom最近在玩一款游戏,有n + 1个柱子在游戏中,从左到右,柱子的编号从0 - n, 第i个柱子的坐标x = i, 在\([n + 1, \infty]\)都有站点,Tom能够赢得游戏,如果跳到这些站点,玩家的起始坐标是0,只能从从左跳到右,坐标必须是递增的,只能跳到柱子上或者站点上,否则她将会失去比赛。另外,他的跳跃能力是受到限制的,并且每一次的跳跃最远距离不超过p, 除了0这根柱子,其他的柱子上都会有宝箱,第i根柱子有\(a_i\)的金币,当然也有陷阱,他将会失去\(\vert{a_i}\vert\), 这个游戏有n层,Tom在第i层如果是跳到柱子上必须是i的倍数(言外之意,如果不跳到柱子上可以直接在p的限制内随意距离跳跃),现在有q次询问,每一次有一个x, 询问在第x层赢得比赛能够获得的最大的金钱数量,如果不能赢得比赛输出Noob


思路:
对于每一层如果x > p那就是不可能的情况,其他的可以dp求出这一层的最优情况,dp[i]代表我到这个位置时我能够获得的最多贡献,dp[i]的注意方程肯定是dp[i] = max(dp[j]) + a[i]其中(j % x == 0 && j < i && i - j <= p)求这种连续滑动的最大最小值可以用单调队列,再来考虑具体的点,题目要求的是赢得比赛,那最后一个状态肯定在\([n + 1, \infty]\),n + 1是能够让最多的点能跳到这个点的点(贪心思想),所以最末的状态是n + 1, 开头在0位置,中间的位置都是x的倍数 \(<=n\), 单调队列头元素就是最值,所以维护好队列里面是单调递减的即可,具体的维护请看代码,q次查询\(n\), 倍数\(log(n)\), 中间又有单调队列的操作,单调队列是基于倍数的所以\(log(n)\), 所以最后的时间复杂度为\(O(n * logn * logn)\), 差点点超时哦!😏


总结:
滑动的区间最大最小值可以用单调队列来维护哦😊

点击查看代码
#include <bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false);
using namespace std;

typedef long long ll;
const ll INF = 0x3f3f3f3f;
const ll MAXN = 1e6 + 10;
ll n, q, p;
struct Node
{
    ll val, pos;
} a[MAXN];
ll dp[MAXN];
map<ll, ll> mp;


int main()
{
	IOS; cin.tie(0), cout.tie(0);
    cin >> n >> q >> p;
    for (int i = 1; i <= n; ++i)
    {
        cin >> a[i].val;
        a[i].pos = i;
    }

    while (q--)
    {
        ll x;
        cin >> x;

        if (mp.count(x))
        {
            cout << mp[x] << endl;
            continue;
        }

        if (x > p)
        {
            cout << "Noob" << endl;
            continue;
        }

        dp[0] = 0;
        vector<ll> v;
        v.push_back(0); //先把0位置放入到可以跳到的点
        for (int i = x; i <= n; i += x)
        {
            v.push_back(i);
        }
        v.push_back(n + 1); //因为最后是跳到[n + 1, 无穷]才算成功,所以最后跳到的点要把这个区间里的一个数作为最终状态,显然所有的情况中n + 1这个状态是最优的,因为n + 1是离能跳过来最近的所以只要讨论最后跳到n + 1的状态即可

        deque<ll> dq;
        dq.push_back(0);    //刚开始只有0位置

        for (int i = 1; i < v.size(); ++i)
        {
            while (dq.size() && v[i] - dq.front() > p)
                dq.pop_front();
            dp[v[i]] = dp[dq.front()] + a[v[i]].val;
            while (dq.size() && dp[dq.back()] < dp[v[i]])
                dq.pop_back();
            dq.push_back(v[i]);        
        }

        mp[x] = dp[n + 1];
        cout << mp[x] << endl;
    }
	return 0;
}

posted @ 2022-08-03 10:14  YUGUOTIANQING  阅读(67)  评论(0)    收藏  举报