TP
由于题目中出现了非常多的1e9范围的数据,所以能够拿来跑循环的一个是n(套餐种类),一个是m(每次哪天租几次),同时 ∑ q i < = 3 e 5 \sum{qi} <= 3e5 ∑qi<=3e5也很令人在意,然后发现几乎不可能再m上直接设置状态,因为时间复杂度会变得非常不合理,所以选择在 ∑ q i \sum{qi} ∑qi上转移,每次租车都属于某一天,那么我们可以根据相隔天数讨论,如果符合可以直接转移,否则在 ∑ q i \sum{qi} ∑qi上二分,由于我们在开始时会对天数排序,那么越往后天数也会越大。知道我们找到一个点,这个点刚好是我们租这种车还没过期的最后一天,我们更新这一天即可,答案就是最后一天的dp值。
非常合理的dp,但是状态设置很巧妙,确实是个非常不错的思维题。
#include "bits/stdc++.h"
using namespace std;
#define int long long
#define pii pair<int,int>
//const int INF = 1e17;
const int mod = 1e6 + 3;
const int N = 3e5;
const int MOD = 1e9 + 7;
int a[N];
struct node {
int per, rent, cot;
} p[N];
struct use {
int d, t;
} u[N];
int dp[N];
int to[N];
int n, m, r;
int tot = 0;
int Find(int y, int day) {
int l = 1;
int rr = tot;
while (l <= rr) {
int mid = (l + rr) >> 1;
if (to[mid] <= day + p[y].per - 1)
l = mid + 1;
else
rr = mid - 1;
}
return l - 1;
}
void solve() {
cin >> n >> m >> r;
for (int i = 1; i <= n; ++i) {
cin >> p[i].per >> p[i].rent >> p[i].cot;
}
for (int i = 1; i <= m; ++i) {
cin >> u[i].d >> u[i].t;
}
sort(u + 1, u + 1 + m, [&](use u1, use u2) {
return u1.d < u2.d;
});
for (int i = 1; i <= m; ++i) {
tot += u[i].t;
for (int j = tot - u[i].t + 1; j <= tot; ++j) {
to[j] = u[i].d;
}
}
memset(dp, 0x3f, sizeof dp);
dp[0] = 0;
for (int i = 0; i < tot; ++i) {
dp[i + 1] = min(dp[i + 1], dp[i] + r);
for (int j = 1; j <= n; ++j) {
if (i + p[j].rent <= tot && to[i + p[j].rent] <= to[i + 1] + (p[j].per - 1))
dp[i + p[j].rent] = min(dp[i + p[j].rent], dp[i] + p[j].cot);
else {
int pos = Find(j, to[i+1]);
dp[pos] = min(dp[pos], dp[i] + p[j].cot);
}
}
}
cout << dp[tot] << endl;
}
signed main() {
// freopen("in.txt", "r", stdin);
ios::sync_with_stdio(0);
solve();
}