传送门
与Codeforces1107G一起食用
思路
- 想到要用dp……然后常规地设dp[i]为推倒前i个牌的最小花费
- 有两种情况:一是当前这个推,二是不推而被别人推。对于第一种,需要找到这个左推(因为扫到这里他是最后一个所以不用右推)的最远处,于是有了预处理每一位的最左边;对于第二种,巨弱鲁莽地优先队列搞了,看大佬代码直接单调栈快了我好几倍,菜不成声。
const int maxn = 25e4 + 5, maxm = 1e7 + 5;
int n, m, k[maxn], q, tot, h[maxm], L[maxm], R[maxm];
ll val[maxm], dp[maxm];
vector<int> a[maxn];
vector<ll> c[maxn];
void Read() {
read(n), read(m);
rep(i, 1, n) {
read(k[i]);
rep(j, 1, k[i]) {
int aa;
read(aa);
a[i].push_back(aa);
}
rep(j, 1, k[i]) {
ll cc;
read(cc);
c[i].push_back(cc);
}
}
for (read(q); q; q--) {
int id, mul;
read(id), read(mul);
rep(i, 1, k[id]) {
h[tot + i] = a[id][i - 1];
val[tot + i] = c[id][i - 1] * mul;
}
tot += k[id];
}
}
void Pre() {
stack<pii> st;
rep(i, 1, m) {
int l = max(1, i - h[i] + 1);
while (!st.empty() && st.top().second >= l) st.pop();
if (st.size() && l <= st.top().first) l = st.top().second;
L[i] = l;
st.push({i, L[i]});
}
while (!st.empty()) st.pop();
irep(i, m, 1) {
int r = min(m, i + h[i] - 1);
while (!st.empty() && st.top().second <= r) st.pop();
if (st.size() && r >= st.top().first) r = st.top().second;
R[i] = r;
st.push({i, R[i]});
}
}
ll Dp() {
typedef pair<ll, int> P;
priority_queue<P, vector<P>, greater<P>> Q;
rep(i, 1, m) {
dp[i] = val[i] + dp[L[i] - 1];
while (!Q.empty() && Q.top().second < i) Q.pop();
if (Q.size()) dp[i] = min(dp[i], Q.top().first);
Q.push({dp[i - 1] + val[i], R[i]});
}
return dp[m];
}
int main() {
ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
Read();
Pre();
writeln(Dp());
return 0;
}