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

版权声明:http://blog.csdn.net/Mitsuha_。 https://blog.csdn.net/Mitsuha_/article/details/82084135

Neko’s loop
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)

Problem Description
Neko has a loop of size n.
The loop has a happy value ai on the i−th ( 0 i n 1 ) grid.
Neko likes to jump on the loop.She can start at anywhere. If she stands at i−th grid, she will get a i happy value, and she can spend one unit energy to go to ((i+k)mod n)−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 m unit energies and she wants to achieve at least s happy value.
How much happy value does she need at least before she jumps so that she can get at least s 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.

Input
The first line contains only one integer 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 ) .
The next line contains n integers, the i−th integer is a i 1 ( 10 9 a i 1 10 9 )

Output
For 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

思路:对于每个起点 i ,走若干步后都会回到起点,即每个 a i 属于其中一个循环节。

求出每个循环节里的数,然后枚举循环节里的起点,求行走获得的快乐最大值。

那么对于行走可能有2种选择:

假设循环节长度为 l e n

一种是先走 m l e n 圈循环(如果走一圈的快乐值总和大于0),然后对于剩下的 m % l e n 步,我们可以用单调队列求其最值。

还有一种是如果 l e n m ,则先用单调队列求出走 l e n 步以内可以获得的最值,
假设在 l e n 步以内需要 x 步才能得到最大值,然后用这个最大值加上 m x l e n ( )

然后对这2种选择取最大值。

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e4+10;
const int MOD=1e9+7;
const double PI=acos(-1.0);
typedef long long ll;
ll a[MAX];
ll sum[MAX];
int v[MAX];
vector<ll>p;
deque<int>que;
ll cal(ll m)
{
    int len=p.size();
    for(int i=0;i<len;i++)p.push_back(p[i]);            //扩大2倍
    for(int i=0;i<p.size();i++)sum[i]=(i==0?p[i]:sum[i-1]+p[i]);
    int y=m%len;
    ll ans=0;
    while(!que.empty())que.pop_back();
    for(int i=0,R=0;i<len;i++)          //先走m/len圈
    {
        while(!que.empty()&&que.back()<i)que.pop_back();
        while(R<=i+y-1)
        {
            while(!que.empty()&&sum[que.front()]<=sum[R])que.pop_front();
            que.push_front(R);
            R++;
        }
        int x=que.back();
        ll res=max(0ll,sum[x]-(i==0?0:sum[i-1]));
        ans=max(ans,res+max(0ll,m/len*sum[len-1]));
    }
    if(m>=len)              //先找到最大值所需步数x,再走(m-x)/len圈
    {
        while(!que.empty())que.pop_back();
        for(int i=0,R=0;i<len;i++)
        {
            while(!que.empty()&&que.back()<i)que.pop_back();
            while(R<=i+len-1)
            {
                while(!que.empty()&&sum[que.front()]<=sum[R])que.pop_front();
                que.push_front(R);
                R++;
            }
            int x=que.back();
            ll res=max(0ll,sum[x]-(i==0?0:sum[i-1]));
            ans=max(ans,res+max(0ll,(m-(x-i+1))/len*sum[len-1]));
        }
    }
    return ans;
}
int main()
{
    int T,cas=1;
    cin>>T;
    while(T--)
    {
        ll n,s,m,k;
        scanf("%lld%lld%lld%lld",&n,&s,&m,&k);
        for(int i=0;i<n;i++)scanf("%lld",&a[i]);
        for(int i=0;i<n;i++)v[i]=0;
        ll ans=s;
        for(int i=0;i<n;i++)
        {
            if(v[i])continue;
            p.clear();
            int j=i;
            while(v[j]==0)      //找出循环节里的数并计算答案
            {
                v[j]=1;
                p.push_back(a[j]);
                j=(j+k)%n;
            }
            ans=min(ans,max(s-cal(m),0ll));
        }
        printf("Case #%d: %lld\n",cas++,ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Mitsuha_/article/details/82084135
今日推荐