1. 程式人生 > >【題解】洛谷P2418 yyy loves OI IV

【題解】洛谷P2418 yyy loves OI IV

轉移 || 暴力 兩種 clu OS AD spa IT

感覺很是妙啊……這題數次誤入歧途...最開始想的二維dp,單調隊列優化;無果,卒。於是沒忍住看了下標簽:暴力枚舉?搜索?於是開始想記憶化搜索。以為會有什麽很強的剪枝之類的;30分,卒。最後終於回到正道上:50 0000的數據,只可能有O(n) & O(nlogn)兩種復雜度吧?在這樣的思想+標簽線段樹的指引下,總算是走向了光明。

暴力,正解的開端。首先考慮最開始的二維dp,轉移方程為:dp[i] = min(dp[k] + 1) (k ∈ 1 ~ i - 1) , 且 i ~ k + 1為合法區間。大部分的時間消耗都在於枚舉找最值+判斷是否合法上。對於這部分的優化,我們先考慮一段合法的區間:要麽相差 <= m, 要麽都是一個人的粉絲。第二種情況明顯特判就行,可以做到O(n), 暫時撇去不談。再看第一種情況並列出式子:1. abs (a[i] - a[j - 1] - b[i] + b[j - 1]) <= m; 2. a[i] - b[i] - m <= a[j - 1] - b[j - 1] <= a[i] - b[i] + m. 到這裏發現,可以用線段樹維護區間的最值,將線段樹建成 a[i] - b[i]的權值線段樹,每次查詢在滿足條件的範圍內的dp最小值就好了。註意要防止爆負數,加上一個大一點的數。

#include <bits/stdc++.h>
using namespace std;
#define INF 1061109567
#define maxn 600000
#define ADD 10000
int n, m, a[maxn], c[maxn], cont = INF, b[maxn], dp[maxn], ans = INF;
int N = 1000000;

struct tree
{
    int l, r, num; 
}T[maxn * 4];

int read()
{
    int x = 0;
    char c;
    c = getchar();
    while
(c < 0 || c > 9) c = getchar(); while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = getchar(); return x; } void Build(int p, int l, int r) { T[p].l = l, T[p].r = r, T[p].num = INF; if(l == r) return; int mid = (l + r) >> 1; Build(p << 1, l, mid), Build(p << 1
| 1, mid + 1, r); } void Getmin(int &x, int y) { if(x > y) x = y; } void update(int p, int x, int num) { if(T[p].l == T[p].r) { Getmin(T[p].num, num); return; } int mid = (T[p].l + T[p].r) >> 1; if(x <= mid) update(p << 1, x, num); else update(p << 1 | 1, x, num); T[p].num = min(T[p << 1].num, T[p << 1 | 1].num); } int query(int p, int l, int r) { int L = T[p].l, R = T[p].r; if(R < l || L > r) return INF; if(l <= L && r >= R) return T[p].num; return min(query(p << 1, l, r), query(p << 1 | 1, l, r)); } int main() { n = read(), m = read(); memset(dp, 0x3f3f3f, sizeof(dp)); Build(1, 1, N); for(int i = 1; i <= n; i ++) { c[i] = read(); a[i] = a[i - 1] + (c[i] == 1); b[i] = b[i - 1] + (c[i] == 2); } dp[0] = 0; update(1, ADD, dp[0]); for(int i = 1; i <= n; i ++) { bool flag = false; if(c[i] == c[i - 1]) dp[i] = cont + 1; else flag = true; Getmin(dp[i], dp[i - 1] + 1); int tem = query(1, a[i] - b[i] - m + ADD, a[i] - b[i] + m + ADD); Getmin(dp[i], tem + 1); if(flag) cont = min(dp[i - 1], dp[i]); else Getmin(cont, dp[i]); update(1, a[i] - b[i] + ADD, dp[i]); } printf("%d\n", dp[n]); return 0; }

【題解】洛谷P2418 yyy loves OI IV