2021-10-13 T1 buy
2021-10-13 T1 buy
題目描述
小 D 家附近新開了一家遊樂園。小 D 經常光顧這家遊樂園,他想要找到最優的購票方案。
小 D 想要進入遊樂園 \(N\) 次,第 \(i\) 次在時刻 \(T_i\),這個遊樂園有兩種購票方式:
-
購買單次票:價格為 \(One\),該票每張只能進入遊樂園一次。
-
購買時限票:時限票一共有 \(K\) 種類型,第 \(i\) 種類型的時限票可以使用的時長為 \(num_i\),
價格為 \(cost_i\)。具體來說,若一張時限票在第 t 時刻被使用,則在 \([t, t + num_i - 1]\) 這個時刻區
間內小 D 都可以進入遊樂園。
這兩種票都可以購買任意多張。記 \(sum_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\)
對於 \(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;
}