Codeforces 1131G(dp)

传送门
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;
}
posted @ 2019-04-10 22:05  AlphaWA  阅读(150)  评论(0编辑  收藏  举报