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

题意:有编号为0~n-1的n个游戏,每个活动都有一个价值(可为负),给你m,s和k,你可以从任意一个编号开始玩,但是下一个游戏必须是编号为(i + k)%n的游戏,你最多能玩m次游戏,问你如果最后你手里要有s的价值,那么你至少一开始要有多少价值。

思路:由裴蜀定理可以知道,如果有n个值首尾相连,间隔为k地走,那么最后会有一个循环节,这样的循环节一共有gcd(n, k)个,每个循环节长度n / gcd(n, k)个。所以我们只要找出所有循环节,并且把每个循环节的最大价值算出来就行了。对于每个循环节,他能获得的最大价值有两种情况,记循环节长度len:

1.m整除len:找不超过len的最大价值,如果走一遍循环节获得价值为正 ,那么再加上sum*(m / len - 1)

2.m不整除len:找不超过m%len的最大价值,如果走一遍循环节获得价值为正 ,那么再加上sum*(m / len);这里有一种情况可能前面提到的状况不是最优,当走一遍循环节为负并且m>len时,问题其实转化为了上面第一种情况

故当次循环节的最大价值为以上两种情况最大值。

关于求“不超过len的最大价值”和“不超过m%len的最大价值”可以用单调队列解决:开两倍循环节长度的数组储存两个循环节,然后变成前缀和,这样我们可以用单调队列维护,用前缀和相减计算出最大价值。

参考:HDU 6444 Neko‘s loop(思维+长度不超过L的最大子段和)

代码:

#include<map>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 20000 + 10;
const int seed = 131;
const int MOD = 1000000000 + 7;
const int INF = 0x3f3f3f3f;
ll a[maxn];
ll arr[maxn], q[maxn];
int vis[maxn];
int gcd(int a, int b){
    return b == 0? a : gcd(b,a % b);
}
ll sum[maxn];   //前缀和
ll cal(int sz, int len){
    ll ret = 0;
    ll head = 0, tail = 0;
    for(int i = 1; i <= len << 1; i++){
        if(i == 1) sum[i] = arr[i];
        else sum[i] = sum[i - 1] + arr[i];
        if(i <= sz) ret = max(ret, sum[i]);
        while(head < tail && q[head] + sz < i) head++;
        if(head < tail) ret = max(ret, sum[i] - sum[q[head]]);
        while(head < tail && sum[q[tail - 1]] >= sum[i]) tail--;
        q[tail++] = i;
    }
    return ret;
}
ll solve(int m, int len){
    ll all = 0;
    for(int i = 1; i <= len; i++)
        all += arr[i];  //总和
    ll ans1 = cal(m % len, len);
    ll ans2 = cal(len, len);
    if(all > 0 && m / len >= 1)
        ans1 += all * 1LL * (m / len);
    if(all > 0 && m / len >= 2)
        ans2 += all * 1LL * (m / len - 1);
    return max(ans1, ans2);
}
int main(){
    int T, Case = 1;
    ll n, m, k, s;
    scanf("%d", &T);
    while(T--){
        scanf("%lld%lld%lld%lld", &n, &s, &m, &k);
        for(int i = 0; i < n; i++){
            scanf("%lld", &a[i]);
            vis[i] = 0;
        }
        int num = gcd(n, k);    //循环节个数
        int len= n / num;      //循环节长度
        ll Max = -INF;
        for(int i = 0, tt = 1; tt <= num && i < n; i++){
            if(vis[i]) continue;
            vis[i] = 1;
            int point = i;
            for(int j = 1; j <= len; j++){
                arr[j] = arr[j + len] = a[point];
                point = (point + k) % n;
                vis[point] = 1;
            }
            Max = max(Max, solve(m, len));
            tt++;
        }
        if(Max >= s)
            printf("Case #%d: 0\n", Case++);
        else
            printf("Case #%d: %lld\n", Case++, s - Max);
    }
    return 0;
}

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

原文地址:https://www.cnblogs.com/KirinSB/p/9551631.html

时间: 2024-12-11 20:42:27

hdu 6444 网络赛 Neko's loop(单调队列 + 裴蜀定理)题解的相关文章

HDU 3410 &amp;&amp; POJ 3776 Passing the Message 单调队列

题意: 给定n长的数组(下标从1-n)(n个人的身高,身高各不相同 问:对于第i个人,他能看到的左边最矮的人下标.(如果这个最矮的人被挡住了,则这个值为0) 还有右边最高的人下标,同理若被挡住了则这个值为0 输出n行,每个人左右2边的下标. 单调队列,对于 front - rear 的队列(注意出队都是在rear,入队也是在rear) 当加入元素x,若这队列是单调递增的,显然q.front() <= x , 反之若>x ,则把队首元素弹掉,这样就能保持单调性. 若弹掉了队首元素,在此题中就相当

[ACM] hdu 3415 Max Sum of Max-K-sub-sequence (单调队列)

高一时,学校组织去韶山游玩,我没去,这次趁着五一,总算去了我心心念念的韶山.其实我知道所有的景点都是差不多的,可是因为电视剧<恰同学少年>,让我对毛泽东有了进一层的了解,所以,我一直都想去看看. 有两个同学一男一女是我理想的旅友,可是女生不想去,而男士回家了.所以,我独自一人去了. 准备工作:一小包饼干,一小包山楂片,两个苹果,一瓶水,帽子(防晒),墨镜(装酷) 早晨5:30起床了,洗漱完毕,吃完早餐,赶到公交车站牌那里,才6点过几分.公交车6:31才到,等了近半个小时(公交车上明明说是6:0

HDU 3415 Max Sum of Max-K-sub-sequence(单调队列)

Problem Description Given a circle sequence A[1],A[2],A[3]......A[n]. Circle sequence means the left neighbour of A[1] is A[n] , and the right neighbour of A[n] is A[1]. Now your job is to calculate the max sum of a Max-K-sub-sequence. Max-K-sub-sequ

hdu 1506 Largest Rectangle in a Histogram 单调队列

Largest Rectangle in a Histogram Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 12554    Accepted Submission(s): 3509 Problem Description A histogram is a polygon composed of a sequence of rec

HDU 4374 One hundred layer DP的单调队列优化

One hundred layer Problem Description Now there is a game called the new man down 100th floor. The rules of this game is: 1.  At first you are at the 1st floor. And the floor moves up. Of course you can choose which part you will stay in the first ti

HDU 5089 Assignment(rmq+二分 或 单调队列)

Assignment Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 557    Accepted Submission(s): 280 Problem Description Tom owns a company and he is the boss. There are n staffs which are numbered fr

HDU 6319 Problem A. Ascending Rating(单调队列)

要求一个区间内的最大值和每次数过去最大值更新的次数,然后求每次的这个值异或 i 的总和. 这个序列一共有n个数,前k个直接给出来,从k+1到n个数用公式计算出来. 因为要最大值,所以就要用到单调队列,然后从后往前扫一遍然后每次维护递减的单调队列. 先把从n-m+1以后开始的数放进单调队列,这时候先不操作,然后剩下的数就是要异或相加的数,然后每次的队首元素就是这个区间内的最大值,这个队列里的元素个数,其实就是更新到最大值的逆过程,也就是最大值需要更新的次数. #include<map> #inc

HDU 5033 Building(北京网络赛B题) 单调栈 找规律

做了三天,,,终于a了... 11724203 2014-09-25 09:37:44 Accepted 5033 781MS 7400K 4751 B G++ czy Building Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Total Submission(s): 1257    Accepted Submission(s): 358 Special Judg

hdu 5067 网络赛 状态压缩 或dfs

题意是给你n*m的方格 里面有最多10个格子有数  问你最少走多少步能将所有的数字移到左上角    能无限装下数字 这里介绍两种做法  dfs和状态压缩dp 1   dfs 由于每个数字之间是一定可以到达的  所有只用考虑走有数字的情况   最多10!种情况  找到做小的就行   果断的深搜       注意下优化 #include<stdio.h> #include<string.h> #include<iostream> using namespace std; s