zoukankan      html  css  js  c++  java
  • Codeforces 487B Strip (ST表+线段树维护DP 或 单调队列优化DP)

    题目链接 Strip

    题意   把一个数列分成连续的$k$段,要求满足每一段内的元素最大值和最小值的差值不超过$s$,

    同时每一段内的元素个数要大于等于$l$,

    求$k$的最小值。

     

    考虑$DP$

    设$dp[i]$为前$i$个数字能划分成区间个数的最小值。

    则$dp[i] = min(dp[j] + 1)$

    于是下一步就是求符合条件的j的范围。

    构建$ST$表,支持区间查询最大值和最小值。

    对于每一个位置$x$,我们知道$max(a[i]...a[x]) - min(a[i]...a[x])$肯定是随着i的减小非递减的。$(i <= x)$

    于是我们就可以通过二分求出符合条件的$max(a[i]...a[x]) - min(a[i]...a[x])$的最小值,记为$c[x]$

    当不存在可以转移到$dp[x]$的$dp[i]$时,$c[x]$为$-1$。

    $n <= 100000$,考虑用线段树优化。

    单点更新,区间查询最小值。

    时间复杂度$O(nlogn)$

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define rep(i, a, b)	for (int i(a); i <= (b); ++i)
    #define dec(i, a, b)	for (int i(a); i >= (b); --i)
    #define	lson		i << 1, L, mid
    #define	rson		i << 1 | 1, mid + 1, R
    
    typedef long long LL;
    
    const int N = 1e5 + 10;
    const int A = 18;
    
    int f[N][A], g[N][A];
    int a[N], lg[N], c[N], dp[N];
    int n, s, l;
    int L, R;
    
    int t[N << 2];
    
    void ST(){
    	rep(i, 1, n) f[i][0] = a[i];
    	rep(j, 1, 17) rep(i, 1, n)
    		if ((i + (1 << j) - 1) <= n) f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
    
    	rep(i, 1, n) g[i][0] = a[i];
    	rep(j, 1, 17) rep(i, 1, n)
    		if ((i + (1 << j) - 1) <= n) g[i][j] = max(g[i][j - 1], g[i + (1 << (j - 1))][j - 1]);
    
    }
    
    inline int solvemin(int l, int r){
    	int k = lg[r - l + 1];
    	return min(f[l][k], f[r - (1 << k) + 1][k]);
    }
    
    inline int solvemax(int l, int r){
    	int k = lg[r - l + 1];
    	return max(g[l][k], g[r - (1 << k) + 1][k]);
    }
    
    inline void pushup(int i){ t[i] = min(t[i << 1], t[i << 1 | 1]);}
    
    void build(int i, int L, int R){
    	if (L == R){ t[i] = 1 << 30; return;}
    	int mid = (L + R) >> 1;
    	build(lson);
    	build(rson);
    	pushup(i);
    }
    
    void update(int i, int L, int R, int x, int val){
    	if (L == R && L == x){ t[i] = min(t[i], val); return;}
    	int mid = (L + R) >> 1;
    	if (x <= mid) update(lson, x, val);
    	else update(rson, x, val);
    	pushup(i);
    }
    
    int query(int i, int L, int R, int l, int r){
    	if (L == l && R == r) return t[i];
    	int mid = (L + R) >> 1;
    	if (r <= mid) return query(lson, l, r);
    	else if (l > mid) return query(rson, l, r);
    	else return min(query(lson, l, mid), query(rson, mid + 1, r));
    }
    
    int main(){
    
    	rep(i, 1, 1e5 + 1) lg[i] = (int)log2((double)(i));
    
    	scanf("%d%d%d", &n, &s, &l);
    	rep(i, 1, n) scanf("%d", a + i);
    
    	ST();
    
    	if (l <= 1) c[1] = 1; else c[1] = -1;
    	rep(i, 2, n){
    		L = 1, R = i - l + 1;
    		if (R < 1){ c[i] = -1; continue; }
    		if (solvemax(R, i) - solvemin(R, i) > s){ c[i] = -1; continue;}
    		while (L + 1 < R){
    			int mid = (L + R) >> 1;
    			if (solvemax(mid, i) - solvemin(mid, i) <= s) R = mid;
    			else L = mid + 1;
    		}
    
    		if (solvemax(L, i) - solvemin(L, i) <= s) c[i] = L;
    		else c[i] = R;
    	}
    
    	dp[0] = 0;
    	rep(i, 1, n) dp[i] = 1 << 30;
    
    	build(1, 1, n + 1);
    	update(1, 1, n + 1, 1, 0);
    
    	rep(i, 1, n){
    		if (c[i] == -1) continue;
    		int now = query(1, 1, n + 1, c[i], i - l + 1);
    		dp[i] = min(dp[i], now + 1);
    		update(1, 1, n + 1, i + 1, dp[i]);
    	}
    
    	if (dp[n] < (1 << 30)) printf("%d
    ", dp[n]);
    	else puts("-1");
    	return 0;
    }
    

    上面这个方法很容易想,但是写起来略有难度。

    其实有一种更好的方法。

    用单调队列来优化。

    对于当前的这个$x$,向后扫描,扫到y的时候如果发现区间$[x, y]$不符合题意了。

    那么其实这个时候$x$这个位置已经没用了。

    因为如果$[x,y]$不符合题意,那么$[x, y + 1]$肯定是不符合题意的。

    于是我们可以用单调队列优化,用集合来维护最大值和最小值。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define rep(i, a, b)	for (int i(a); i <= (b); ++i)
    #define dec(i, a, b)	for (int i(a); i >= (b); --i)
    
    const int N = 1e5 + 10;
    
    int n, s, l, now;
    int a[N], f[N];
    multiset <int> st, v;
    
    
    int main(){
    
    	scanf("%d%d%d", &n, &s, &l);
    	rep(i, 1, n) scanf("%d", a + i);
    
    	now = 1; 
    	f[0] = 0;
    	rep(i, 1, n){
    		f[i] = 1 << 30;
    		st.insert(a[i]);
    		if (i - now + 1 >= l) v.insert(f[i - l]);
    		while (*st.rbegin() - *st.begin() > s){
    			st.erase(st.find(a[now]));
    			auto it = v.find(f[now - 1]);
    			if (it != v.end()) v.erase(it);
    		//	if (i - now + 1 >= l) v.erase(v.find(f[now - 1]));
    			++now;
    		}
    		if (!v.empty()) f[i] = *v.begin() + 1;
    	}
    
    	printf("%d
    ", f[n] >= (1 << 30) ? -1 : f[n]);
    	return 0;
    }
    
  • 相关阅读:
    hdoj--1162--Eddy's picture(最小生成树)
    hdoj--1087--Super Jumping! Jumping! Jumping!(贪心)
    hdoj--1051--Wooden Sticks(LIS)
    hdoj--5532--Almost Sorted Array(正反LIS)
    CodeForces--609C --Load Balancing(水题)
    poj--2631--Roads in the North(树的直径 裸模板)
    CodeForces--606A --Magic Spheres(模拟水题)
    CodeForcess--609B--The Best Gift(模拟水题)
    hdoj--1201--18岁生日(模拟)
    poj--1985--Cow Marathon(树的直径)
  • 原文地址:https://www.cnblogs.com/cxhscst2/p/7515611.html
Copyright © 2011-2022 走看看