HDU 6444 Neko's loop(单调队列)

Neko has a loop of size nn. 
The loop has a happy value aiai on the i−th(0≤i≤n−1)i−th(0≤i≤n−1) grid. 
Neko likes to jump on the loop.She can start at anywhere. If she stands at i−thi−thgrid, she will get aiai happy value, and she can spend one unit energy to go to ((i+k)modn)−th((i+k)modn)−th grid. If she has already visited this grid, she can get happy value again. Neko can choose jump to next grid if she has energy or end at anywhere.
Neko has mm unit energies and she wants to achieve at least ss happy value. 
How much happy value does she need at least before she jumps so that she can get at least ss happy value? Please note that the happy value which neko has is a non-negative number initially, but it can become negative number when jumping.

InputThe first line contains only one integer T(T≤50)T(T≤50), which indicates the number of test cases. 
For each test case, the first line contains four integers n,s,m,k(1≤n≤104,1≤s≤1018,1≤m≤109,1≤k≤n)n,s,m,k(1≤n≤104,1≤s≤1018,1≤m≤109,1≤k≤n). 
The next line contains nn integers, the i−thi−th integer is ai−1(−109≤ai−1≤109)ai−1(−109≤ai−1≤109) 
OutputFor each test case, output one line "Case #x: y", where x is the case number (starting from 1) and y is the answer.Sample Input

2
3 10 5 2
3 2 1
5 20 6 3
2 3 2 1 5

Sample Output

Case #1: 0
Case #2: 2

题意:给n个数字,当位于某一个数字时,可以得到这个数字的快乐值(可以重复获得),可以走m步,每次向后跳k格(循环),问快乐值要达到s,那么初始快乐值的最小值是多少?思路:这是2018ccpc的网络赛的题,暑假看着学长打比赛,在傍边连读题都帮不上。。。。

首先有一个显而易见的结论,对于某一个起点,跳着跳着就会回到起点,也就是说,一定存在循环节。其次显而易见的,就是对于某一个数,只会存在于一个循环节中。那么如果不考虑循环节的起点差异,找出所有循环节就是O(n)的。而实际上我们确实不用考虑,原因接下来再说。

我们可以轻易的算出某个循环节中所有元素的和--sum,因为存在负数,所以sum可能是负数。再声明一下,len是循环节长度。若sum<0,那么最多只需走min(m,len)步就可以得到最优解。若sum>0,则需要先走m/len-1圈,为什么减一,其他博客有详解(博主太懒了),然后最多走m-(m/len-1)*len)步
先说这后面多出来的步数,假设是p步,这p步在sum<0时,p<=len,sum<0时,len<=p<=2*len。为了求出这p步走出的最大值,我们使用单调队列。首先求出循环节的前缀和sum,单调队列维护长度为p的滑窗的sum最小值。维护  ans =( sum[i]-单调队列最小值 ) 的最大值就可以啦!由于p的长度,要把循环节扩展3倍哟。

然后我们看到,这个单调队列,在ans取得最大值时,单调队列的最小值位置是不定的,所以很容易想到,不一定就是循环节找出来时的起点,所以我们很容易想到,这一个过程,相当于已经枚举了实际问题中的起点。也就是,我们在取ans的最大值时的最小值位置,就可以当实际问题中的起点。

#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#define fuck(x) cout<<#x<<" = "<<x<<endl;
#define debug(a,i) cout<<#a<<"["<<i<<"] = "<<a[i]<<endl;
#define ls (t<<1)
#define rs ((t<<1)+1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 100086;
const int maxm = 100086;
const int inf = 2.1e9;
const ll Inf = 999999999999999999;
const int mod = 1000000007;
const double eps = 1e-6;
const double pi = acos(-1);
ll num[maxn];
vector<ll>vec;
bool vis[maxn];
ll sum[maxn];
ll n,s,m,k;
int len;
struct node
{
    ll x;
    int id;
};
deque<node>q;
ll solve(int p){
    q.clear();
    for(int i=0;i<len;i++){
        vec.push_back(vec[i]);
    }
    for(int i=0;i<len;i++){
        vec.push_back(vec[i]);
    }
    ll ans=0;
    sum[0]=vec[0];
    for(int i=1;i<len*3;i++){
        sum[i]=sum[i-1]+vec[i];
    }
    for(int i=0;i<len*3;i++){
        while(!q.empty()&&q.back().x>sum[i]){
            q.pop_back();
        }
        if(!q.empty()&&q.front().id<i-p){q.pop_front();}
        q.push_back(node{sum[i],i});
        ans=max(ans,1ll*sum[i]-q.front().x);
    }
    return ans;
}

int main()
{
    int T;
    scanf("%d",&T);
    int cases=0;
    while(T--){
        ll ans=0;
        cases++;
        scanf("%lld%lld%lld%lld",&n,&s,&m,&k);
        for(int i=1;i<=n;i++){
            scanf("%lld",&num[i]);
        }
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++){
            vec.clear();
            if(vis[i]){continue;}
            int pos=i;
            for(int j=1;j<=m;j++){
                vec.push_back(num[pos]);
                vis[pos]=true;
                pos+=k;
                if(pos>n){pos%=n;}
                if(vis[pos]){break;}
            }
            len=vec.size();
            ll sum=0;
            for(int i=0;i<len;i++){
                sum+=vec[i];
            }
            if(sum<0){
                ans=max(ans,solve(len));
            }
            else{
                ans=max(ans,solve(m-(m/len-1)*len)+(m/len-1)*sum);
            }
        }
        printf("Case #%d: %lld\n",cases,max(0ll,s-ans));
    }
    return 0;
}

HDU 6444 Neko's loop(单调队列)

原文地址:https://www.cnblogs.com/ZGQblogs/p/10806800.html

时间: 2024-08-28 22:21:13

HDU 6444 Neko's loop(单调队列)的相关文章

hdu 6444 Neko&#39;s loop 线段树区间更新

题目连接:Neko's loop 题意:给一个长度为n的环,下标从0~n-1,环上每个点有个值表示到这个点会得到的快乐值.,然后每次可以花费1能量往后跳k步.你可以选择任意点开始跳,可以任意点结束,最多跳m次问得到至少s的快乐值最初要拥有多少. 题解:先把循环节挑出来,,然后在循环节上找最大字段和.循环节长度为cnt,然后就是枚举起点用线段树维护前缀和,然后取最大值. #include<bits/stdc++.h> #define ls l,m,rt<<1 #define rs m

HDU 6047 Maximum Sequence (贪心+单调队列)

题意:给定一个序列,让你构造出一个序列,满足条件,且最大.条件是 选取一个ai <= max{a[b[j], j]-j} 析:贪心,贪心策略就是先尽量产生大的,所以就是对于B序列尽量从头开始,由于数据比较大,采用桶排序,然后维护一个单调队列,使得最头上最大. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <string> #i

HDU Non-negative Partial Sums (单调队列)

Problem Description You are given a sequence of n numbers a0,..., an-1. A cyclic shift by k positions (0<=k<=n-1) results in the following sequence: ak ak+1,..., an-1, a0, a1,..., ak-1. How many of the n cyclic shifts satisfy the condition that the

hdu 2191 (多重背包的单调队列优化)

多重背包单调队列优化是思想是.普通的dp为 dp[i][j]=max{dp[i-1][j-k*v[i]]+k*w[i]}; 其实你可以发现对能更新j是j和一个剩余类.也就是 0, v[i],2v[i],3v[i] ,4v[i]... 1 ,1+v[i],1+2v[i],1+3v[i] ........... v[i]-1,2*v[i]-1...... 更新值存在一个剩余类中,组与组之间不存在更新.那么实际上我们可以写dp写好这样 dp[b+x*v[i]]=max{ dp[b+k*v[i]]+(x

HDU 5380 Travel with candy 单调队列

链接 题解链接:http://www.cygmasot.com/index.php/2015/08/16/hdu_5380 题意: n C 一条数轴上有n+1个加油站,起点在0,终点在n.车的油箱容量为C 下面n个数字表示每个加油站距离起点的距离. 下面n+1行表示每个加油站买进和卖出一单位油的价格.油可以买也可以卖. 问开到终点的最小花费. 思路: 把油箱保持装满,然后维护一个价格单调递增的队列. #pragma comment(linker, "/STACK:1024000000,10240

hdu 5945 Fxx and game 单调队列优化dp

Fxx and game Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others) Problem Description Young theoretical computer scientist Fxx designed a game for his students. In each game, you will get three integers X,k,t.In each s

hdu 6444 网络赛 Neko&#39;s loop(单调队列 + 裴蜀定理)题解

题意:有编号为0~n-1的n个游戏,每个活动都有一个价值(可为负),给你m,s和k,你可以从任意一个编号开始玩,但是下一个游戏必须是编号为(i + k)%n的游戏,你最多能玩m次游戏,问你如果最后你手里要有s的价值,那么你至少一开始要有多少价值. 思路:由裴蜀定理可以知道,如果有n个值首尾相连,间隔为k地走,那么最后会有一个循环节,这样的循环节一共有gcd(n, k)个,每个循环节长度n / gcd(n, k)个.所以我们只要找出所有循环节,并且把每个循环节的最大价值算出来就行了.对于每个循环节

HDU 4122 Alice&#39;s mooncake shop 单调队列优化dp

Alice's mooncake shop Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=4122 Description The Mid-Autumn Festival, also known as the Moon Festival or Zhongqiu Festival is a popular harvest festival celebrated by Ch

HDU 3415 Max Sum of Max-K-sub-sequence 单调队列题解

本题又是一题单调队列题解. 技巧就是需要计算好前n项和Sn = a1 + a2 + ... an 这样方便处理. 记录一条单调队列,其意义是: q(head), q(head+1), ...q(tail) 其中头q(head)代表当前最佳解的起点 这样我们只需要在求某点为结尾的S[i] - S[q(head)就得到当前最佳值. 了解了单调数列,知道其中的记录意义,那么这道题就没有难度了.我也是了解这些信息之后就自己敲出代码的. 不过有些细节没写好也让我WA了几次. 最近少刷水题,而一直都是每天一