HDU6444 Neko's loop(2018CCPC網絡賽,線段樹,思路)

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 ai happy value, and she can spend one unit energy to go to ((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 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 ai−1(−109≤ai−1≤109)

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

思路

首先說題意,有一個長度爲n 的循環序列,序列的每個位置有一個值,當你跳到這個位置,就會獲得這個位置上的值的積分,你可以跳不超過m 次,但是每次必須隔k 個跳一下,現在你想要獲得至少s 積分,問你需要至少獲得多少積分加上從這個循環序列裏面得到的積分總和不小於s ,實質上就是求出能在這個環上獲得的最大值即可。

測試數據給出方式爲,首先是t 組測試數據,然後給出四個數n,s,m,k ,然後接下來一行是這個n 個數具體的值.

首先,對於一個環,每次只能走k 步,一定存在循環節。由裴蜀定理我們可以得到,循環節的個數cnt=gcd(n,k) ,循環節的長度爲len=n/cnt

我們的首要思路就是求出每個循環節上面的最大值。

那麼對於每一個循環節,應該如何來求最大值?

對於每一個循環節,我們可以從這個循環節上的任意位置開始跳,跳的次數不超過m 次,那麼對於當前這個循環節所連成的環,我們可以求出一個前綴和sum[]數組出來,只要存在sum[len]>0 那麼就證明不論從循環節上的哪一個位置開始走,走完這一個循環節,我們所獲得的權值一定是一個正值。

我們可以假設:

  • m>=len 的時候有兩種情況:

    1. 此時走的步數大於循環節的長度,當sum[len]>0 時,我們就儘量多的走這個循環節,可以獲得的價值爲sum[len]*(m/len),此時還剩下m%len 的長度沒有處理。我們接着處理剩下的長度,首先建立一棵線段樹,維護一下sum[]數組的最小值,因爲我們由前綴和可以計算出任意一個區間的和(sum[l...r]=sum[r]-sum[l-1]),我們已經知道當前區間的右端點sum[r],要使得和最大就要使得sum[l-1]的值儘量小,所以用線段樹找一下長度爲m%len 的最大值。
    2. 第二種情況我們把從長度分成兩個區間,第一個是[1...len],第二個是[len+1...m],和上面一樣,我們求出[len+1...m]最多包含幾個len,計算出結果爲sum[len]*(M/len).接下來剩下的長度就是[1..len]了,我們利用線段樹找出這個的最大值。然後兩種情況求比較大的一個
  • m<len 時:

    這種情況就是我們在m>=len 的第一種情況,計算m%len 的時候已經包含進去了,不需要再計算。

代碼

#include <bits/stdc++.h>
using namespace std;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define mem(a, b) memset(a, b, sizeof(a))
typedef long long ll;
const ll N = 2e5 + 10;
const ll inf = 1e18;
ll n, m, s, k, a[N], b[N], sum[N], cnt, len, MIN[N << 2];
void pushup(ll rt)
{
    MIN[rt] = min(MIN[rt << 1], MIN[rt << 1 | 1]);
}
void build(ll l, ll r, ll rt)
{
    if (l == r)
    {
        MIN[rt] = sum[l];
        return;
    }
    ll m = (l + r) >> 1;
    build(lson);
    build(rson);
    pushup(rt);
}
ll query(ll L, ll R, ll l, ll r, ll rt)
{
    if (L <= l && r <= R)
        return MIN[rt];
    ll m = (l + r) >> 1;
    ll ans = inf;
    if (L <= m)
        ans = min(ans, query(L, R, lson));
    if (R > m)
        ans = min(ans, query(L, R, rson));
    return ans;
}
ll get_max(ll pos)
{
    ll ans = 0, mm = m;
    for (ll i = 1; i <= len; i++)
    {
        b[i] = b[i + len] = a[pos];
        pos = (pos + k) % n;
    }
    for (ll i = 1; i <= 2 * len; i++)
        sum[i] = sum[i - 1] + b[i];
    build(1, 2 * len, 1);
    ll res1 = 0, res2 = 0;
    if (sum[len] > 0)
        res1 = sum[len] * (mm / len);
    mm %= len;
    for (ll i = len + 1; i <= 2 * len; i++)
        res2 = max(res2, sum[i] - query(i - mm, i, 1, 2 * len, 1));
    ans = res1 + res2;
    if (m > len)
    {
        mm = (m - len);
        if (sum[len] > 0)
            res1 = sum[len] * (mm / len);
        mm = len;
        for (ll i = len + 1; i <= 2 * len; i++)
            res2 = max(res2, sum[i] - query(i - mm, i, 1, 2 * len, 1));
        ans = max(ans, res1 + res2);
    }
    return ans;
}
void solve()
{
    scanf("%lld%lld%lld%lld", &n, &s, &m, &k);
    for (ll i = 0; i < n; i++)
        scanf("%lld", &a[i]);
    cnt = __gcd(n, k), len = n / cnt;
    ll ans = 0;
    for (ll i = 0; i < cnt; i++)
        ans = max(ans, get_max(i));
    ans = ans > s ? 0 : s - ans;
    printf("%lld\n", ans);
}
int main()
{
    //freopen("in2.txt", "r", stdin);
    ll t, q = 1;
    scanf("%lld", &t);
    while (t--)
    {
        printf("Case #%lld: ", q++);
        solve();
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章