zoukankan      html  css  js  c++  java
  • 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
    ", Case++);
            else
                printf("Case #%d: %lld
    ", Case++, s - Max);
        }
        return 0;
    }
  • 相关阅读:
    hdu 5101 Select
    hdu 5100 Chessboard
    cf B. I.O.U.
    cf C. Inna and Dima
    cf B. Inna and Nine
    cf C. Counting Kangaroos is Fun
    Radar Installation 贪心
    spfa模板
    Sequence
    棋盘问题
  • 原文地址:https://www.cnblogs.com/KirinSB/p/9551631.html
Copyright © 2011-2022 走看看