1. 程式人生 > 其它 >2021-10-13 T1 buy

2021-10-13 T1 buy

2021-10-13 T1 buy

題目描述

小 D 家附近新開了一家遊樂園。小 D 經常光顧這家遊樂園,他想要找到最優的購票方案。
小 D 想要進入遊樂園 \(N\) 次,第 \(i\) 次在時刻 \(T_i\),這個遊樂園有兩種購票方式:

  1. 購買單次票:價格為 \(One\),該票每張只能進入遊樂園一次。

  2. 購買時限票:時限票一共有 \(K\)​ 種類型,第 \(i\)​ 種類型的時限票可以使用的時長為 \(num_i\)​,
    價格為 \(cost_i\)​。具體來說,若一張時限票在第 t 時刻被使用,則在 \([t, t + num_i - 1]\)​ 這個時刻區
    間內小 D 都可以進入遊樂園。
    這兩種票都可以購買任意多張。記 \(sum_i\)

    ​ 為小 D 前 \(i\)​ 次進入遊樂園的最小花費(不考慮
    \(i\)​ 次之後的進入),小 D 定義第 \(i\)​ 次進入遊樂園的代價為 \(spend_i = sum_i - sum_{i- 1}\)​。小 D 想
    要知道每次進入遊樂園的代價,即 \(spend_1\)​, ..., \(spend_n\)​。

資料範圍及約定

對於前 \(10\%\) 的資料,\(K = 0\);
對於前 \(40\%\) 的資料,\(K\leq 2\), \(N\leq 10^3\);
對於前 \(60\%\) 的資料,\(K\leq 2\), \(N\leq 10^5\);
對於前 \(80\%\) 的資料,\(K\leq 30\)

, \(N\leq 10^5\);
對於 \(100\%\) 的資料,\(K\leq 500\), \(N\leq 10^5\), \(1\leq Ti, numi, costi, One\leq 10^9\)\(T_i\) 單增。

題目分析

對於前 \(10\%\)​ 的資料,每個時間點只能使用單次票,直接輸出單次票價即可。

對於前 \(40\%\)​ 的資料,列舉在每個時間點使用哪種票,顯然以該時間點作為該票的時間區間終點最優。暴力向前尋找第一個不足以達到當前時間點的時間點,更新 \(sum_i\)​ 即可,時間複雜度 \(O(kn^2)\)​​。

memset(sum,0x7f,sizeof(sum));
sum[0] = 0;
for(int i = 1;i <= n;i++)
{
    for(int j = 0;j <= k;j++)
    {
        int pos = 0;
        for(int p = i - 1;p >= 1;p--)
        if(ti[p] + a[j].len-1 < ti[i]){pos = p;break;}
        sum[i] = min(sum[i],sum[pos] + a[j].pri);
    }
}
for(int i = 1;i <= n;i++)
    printf("%lld\n",sum[i] - sum[i-1]);

對於前 \(80\%\)的資料,考慮優化,在向前尋找第一個不足以到達當前點的時間點時,注意到答案位置顯然具有單調性,二分即可,時間複雜度 \(O(knlogn)\)

memset(sum,0x7f,sizeof(sum));
sum[0] = 0;
for(int i = 1;i <= n;i++)
{
    for(int j = 0;j <= k;j++)
    {
        int pos = 0,l = 0,r = i;
        while(r - l > 1)
        {
            int mid = (l + r) >> 1;
            if(ti[mid] + a[j].len - 1 < ti[i]) l = mid,pos = mid;
            else r = mid;
        }
        sum[i] = min(sum[i],sum[pos] + a[j].pri);
    }
}
for(int i = 1;i <= n;i++)
    printf("%lld\n",sum[i] - sum[i-1]);

對於 \(100\%\) 的資料,考慮進一步優化,注意到顯然對於每一種票,第一個不足以到達當前時間點的位置隨著時間點而遞增,使用雙指標的思想,對於每種票記錄一個 \(pos_j\) 表示上一次的第一個位置,可保證指標前移只執行 \(kn\) 次,時間複雜度 \(O(kn)\)

sum[0] = 0;
for(int i = 1;i <= n;i++)
{
    sum[i] = sum[i-1] + a[0].pri;
    for(int j = 1;j <= k;j++)
    {
        while(ti[pos[j]] + a[j].len - 1 < ti[i]) pos[j]++;
        sum[i] = min(sum[i],sum[pos[j]-1] + a[j].pri);
    }
}
for(int i = 1;i <= n;i++)
    printf("%lld\n",sum[i] - sum[i-1]);

附完整程式碼

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100000 + 10;
struct node
{
    ll len,pri;
}a[N];
int n,k,pos[N];
ll ti[N];
ll sum[N];
signed main()
{
    freopen("buy.in","r",stdin);
    freopen("buy.out","w",stdout);
    scanf("%d%d",&n,&k);
    for(int i = 1;i <= n;i++)
        scanf("%lld",&ti[i]);
    for(int i = 1;i <= k;i++)
        scanf("%lld%lld",&a[i].len,&a[i].pri);
    scanf("%lld",&a[0].pri);
    if(k == 0)
    {
        for(int i = 1;i <= n;i++)
            printf("%lld\n",a[0].pri);
        return 0;
    }
    if(n <= 1000)
    {
        memset(sum,0x7f,sizeof(sum));
        sum[0] = 0;
        for(int i = 1;i <= n;i++)
        {
            for(int j = 0;j <= k;j++)
            {
                int pos = 0;
                for(int p = i - 1;p >= 1;p--)
                    if(ti[p] + a[j].len-1 < ti[i]){pos = p;break;}
                sum[i] = min(sum[i],sum[pos] + a[j].pri);
            }
        }
        for(int i = 1;i <= n;i++)
            printf("%lld\n",sum[i] - sum[i-1]);
        return 0;
    }
    if(k <= 30)
    {
        memset(sum,0x7f,sizeof(sum));
        sum[0] = 0;
        for(int i = 1;i <= n;i++)
        {
            for(int j = 0;j <= k;j++)
            {
                int pos = 0,l = 0,r = i;
                while(r - l > 1)
                {
                    int mid = (l + r) >> 1;
                    if(ti[mid] + a[j].len - 1 < ti[i]) l = mid,pos = mid;
                    else r = mid;
                }
                //cout << pos << endl;
                sum[i] = min(sum[i],sum[pos] + a[j].pri);
            }
        }
        for(int i = 1;i <= n;i++)
            printf("%lld\n",sum[i] - sum[i-1]);
        return 0;
    }
    sum[0] = 0;
    for(int i = 1;i <= n;i++)
    {
    	sum[i] = sum[i-1] + a[0].pri;
        for(int j = 1;j <= k;j++)
        {
            while(ti[pos[j]] + a[j].len - 1 < ti[i]) pos[j]++;
            sum[i] = min(sum[i],sum[pos[j]-1] + a[j].pri);
        }
    }
    for(int i = 1;i <= n;i++)
        printf("%lld\n",sum[i] - sum[i-1]);
    return 0;
}