CF1131G Most Dangerous Shark

link
單調棧+單調隊列+\(dp\)好題。數組

首先一眼看出是 \(dp\)。令 \(dp[i]\) 爲推倒前 \(i\) 個骨牌所耗費的最小价值,(這個並無單調性。
考慮 \(dp[i]\) 由什麼轉移來。數據結構

1.左邊的翻過來(至關於本身向右邊翻)。因爲能轉移過來的 \(j\) 並非連續的(顯然),因此在處理 \(dp[i]\) 時不是很好維護,不妨在處理 \(dp[j]\) 時更新 \(dp[i]\)。不妨設 \(i\) 向左翻能翻倒的最遠位置爲 \(L[i]\),向右爲 \(R[i]\)。一個很重要的性質:全部的 \([i,R[i]],[L[i],i]\) 要麼相離,要麼包含。
則 \(dp[i]=min(dp[i],dp[j-1]+a[j])(j \le i\le R[j])\)。ide

2.本身向左邊翻,則 \(dp[i]=min(dp[i],dp[j-1]+a[i])(L[i]\le j \le i)\)。
細心的讀者能夠發現,第 \(1\) 種狀況就是向右翻,因此轉移並無漏掉什麼。spa

你會發現以上全部的東西、全部的轉移均可以用線段樹/樹狀數組維護,這裏不展開。code

關於 \(L,R\) 數組的維護,乍一看用個單調棧/單調隊列。發現這題用普通的單調數據結構是沒有單調性的,可是爲了追求線性複雜度只能拋棄二分。可是這題有一個特殊的性質:若求 \(L[i]\) 時,當前 \(j>=i-h[i]\),那麼在單調棧/隊列中就能夠直接刪除(顯然),這就保證了線性複雜度,因爲 \(j\) 爲後進先出,用單調棧便可,求 \(R[i]\) 時同理。隊列

關於第 \(1\) 種狀況,單調隊列維護便可。注意這裏的 \(R[i]\) 雖然沒有單調性,可是有局部單調性,即對於相鄰的衆多區間,他們內部單降(由於全部的 \([i,R[i]]\) 要麼相離要麼包含)。因此可用單調棧維護。第 \(2\) 種能夠稍微偷懶一下,\(dp[i]=dp[L[i]-1]+a[i]\),能夠證實是最優的,本身yy一下吧。get

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <map>
#include <queue>
#define LL long long
using namespace std;
const int MAXN = 1e7 + 5, MAXM = 250005, inf = 0x3f3f3f3f;
int m, n, k, tot, h[MAXN], que[MAXN], L[MAXN], R[MAXN], head, tail;
int que1[MAXN], head1, tail1;
vector <int> v1[MAXM], v2[MAXM];
LL a[MAXN], dp[MAXN];
void read(int &x) {
	x = 0; int f = 1; char c = getchar();
	for(; c < '0' || c > '9'; c = getchar()) if(c == '-') f = 0;
	for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
	x = f ? x : -x;
}
int Min(int x, int y) { return x < y ? x : y; }
int Max(int x, int y) { return x > y ? x : y; }
LL MinLL(LL x, LL y) { return x < y ? x : y; }
LL MaxLL(LL x, LL y) { return x > y ? x : y; }
LL Calc(int x) { return a[x] + dp[x - 1]; }
int main() {
	int x, y;
	read(m); read(n);
	for(int i = 1; i <= m; i ++) {
		read(k);
		for(int j = 1; j <= k; j ++) read(x), v1[i].push_back(x);
		for(int j = 1; j <= k; j ++) read(y), v2[i].push_back(y);
	}
	read(k);
	for(int i = 1; i <= k; i ++) {
		read(x); read(y);
		for(int j = 0; j < v1[x].size(); j ++) h[++ tot] = v1[x][j], a[tot] = (LL)v2[x][j] * y, h[tot] --;//, printf("|%d %lld\n", h[tot], a[tot]);
	}
	head = 1; tail = 0; n = tot;
	for(int i = 1; i <= n; i ++) { // 單調棧 
		L[i] = i;
		while(head <= tail && i - h[i] <= que[tail]) L[i] = Min(L[i], L[que[tail]]), tail --;
		que[++ tail] = i;
	}
	head = 1; tail = 0;
	for(int i = n; i >= 1; i --) {
		R[i] = i; 
		while(head <= tail && i + h[i] >= que[tail]) R[i] = Max(R[i], R[que[tail]]), tail --;
		que[++ tail] = i;
	}
//	for(int i = 1; i <= n; i ++) printf("|%d %d|\n", L[i], R[i]);
	head = 1; tail = 0; dp[0] = 0;
	for(int i = 1; i <= n; i ++) {
		dp[i] = dp[L[i] - 1] + a[i];
		
		while(tail1 && R[que1[tail1]] < i) tail1 --; // case1
		que1[++ tail1] = i;
		while(tail1 > 1 && Calc(que1[tail1]) > Calc(que1[tail1 - 1])) tail1 --;
		if(tail1) dp[i] = MinLL(dp[i], Calc(que1[tail1]));
//		if(tail1) printf("|%d->%d %lld|\n", que1[tail1], i, MinLL(dp[i], Calc(que1[tail1])));
		printf("%lld|", dp[i]);
	}
	printf("%lld", dp[n]);
	return 0;
}
相關文章
相關標籤/搜索