zoukankan      html  css  js  c++  java
  • BZOJ 4540 [Hnoi2016]序列 (单调栈 + ST表 + 莫队算法)

    题目链接  BZOJ4540

    考虑莫队算法。

    这题难在$[l, r]$到$[l, r+1]$的转移。

    根据莫队算法的原理,这个时候答案应该加上

    $cal(l, r+1) + cal(l+1, r+1) + cal(l+2, r+1) + ... + cal(r+1, r+1)$

    $cal(l, r)$表示$a[l], a[l+1], a[l+2], ..., a[r]$中的最小值。

    我们先求出$[l, r +1]$ 这些数中的最小值$a[x]$

    那么$cal(l, r+1) + cal(l+1, r+1) + cal(l+2, r+1) + ... + cal(x, r+1)$这一部分就解决了

    这一部分的值为$(x - l + 1) * a[x]$

    剩下还有一部分$cal(x+1, r+1) + cal(x+2, r+1) + ... + cal(r+1, r+1)$

    考虑用单调栈求出两个数组$lc[], rc[]$。

    $lc[i]$表示$a[i]$左边第一个小于$a[i]$的数的位置(如果没有则为$0$)

    $rc[i]$表示$a[i]$右边第一个小于$a[i]$的数的位置(如果没有则为$n+1$)

    然后我们维护两个序列$s1[], s2[]$

    $s1[i] = s1[lc[i]] + (i - lc[i]) * a[i]$

    刚刚那个剩余的情况中,$s1[r+1] - s1[x]$即为这部分的答案。

    因为满足$a[r+1]>=a[x]$,所以对于$a[r+1]$来说,他往左不断找第一个小于他的数,

    肯定能在某一步找到$a[x]$.

    而我们维护$s1[]$的目的就是把这个跳的过程做到$O(1)$.

    转移就是这样

    那么另外三种情况也差不多,以此类推。

    时间复杂度$O(n^{frac{3}{2}}logn)$

    #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)
    
    typedef long long LL;
    
    const int N = 1e5 + 10;
    const int A = 20;
    
    int n, m;
    int lc[N], rc[N], lg[N], belong[N];
    int f[N][A];
    int bs, l, r, x;
    stack <int> s;
    LL ans;
    LL a[N];
    LL s1[N], s2[N];
    LL ret[N];
    
    struct node{
    	int l, r, id;
    	friend bool operator < (const node &a, const node &b){
    		return belong[a.l] == belong[b.l] ? a.r < b.r : belong[a.l] < belong[b.l];
    	}
    } q[N];
    
    inline int Min(int x, int y){ return a[x] < a[y] ? x : y;}
    
    void ST(){
    	rep(i, 1, n) f[i][0] = i;
    	rep(j, 1, 18) 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]);
    }
    
    inline int solve(int l, int r){
    	int k = lg[r - l + 1];
    	return Min(f[l][k], f[r - (1 << k) + 1][k]);
    }
    
    int main(){
    
    	rep(i, 2, 1e5) lg[i] = lg[i >> 1] + 1;
    
    	scanf("%d%d", &n, &m);
    	rep(i, 1, n) scanf("%lld", a + i);
    
    	ST();
    	rep(i, 1, n){
    		while (!s.empty() && a[s.top()] >= a[i]) s.pop();
    		if (s.empty()) lc[i] = 0;
    		else lc[i] = s.top();
    		s.push(i);
    	}
    
    	while (!s.empty()) s.pop();
    	dec(i, n, 1){
    		while (!s.empty() && a[s.top()] >= a[i]) s.pop();
    		if (s.empty()) rc[i] = n + 1;
    		else rc[i] = s.top();
    		s.push(i);
    	}
    
    	rep(i, 1, n) s1[i] = s1[lc[i]] + (i - lc[i]) * a[i];
    	dec(i, n, 1) s2[i] = s2[rc[i]] + (rc[i] - i) * a[i];
    
    	bs = sqrt(n);
    	rep(i, 1, n) belong[i] = (i - 1) / bs + 1;
    
    	rep(i, 1, m){
    		scanf("%d%d", &q[i].l, &q[i].r);
    		q[i].id = i;
    	}
    
    	sort(q + 1, q + m + 1);
    	l = 1, r = 0, ans = 0;
    	rep(i, 1, m){
    		while (r < q[i].r){
    			++r;
    			x = solve(l, r);
    			ans += ((x - l + 1) * a[x] + s1[r] - s1[x]);
    		}
    
    		while (r > q[i].r){
    			x = solve(l, r);
    			ans -= ((x - l + 1) * a[x] + s1[r] - s1[x]);
    			--r;
    		}
    
    		while (l > q[i].l){
    			--l;
    			x = solve(l, r);
    			ans += ((r - x + 1) * a[x] + s2[l] - s2[x]);
    		}
    
    		while (l < q[i].l){
    			x = solve(l, r);
    			ans -= ((r - x + 1) * a[x] + s2[l] - s2[x]);
    		       	++l;
    		}
    
    		ret[q[i].id] = ans;
    	}
    
    	rep(i, 1, m) printf("%lld
    ", ret[i]);
    	return 0;
    }
    
  • 相关阅读:
    Delphi ADOQuery连接数据库的查询、插入、删除、修改
    Delphi开发的一些技巧
    获取的数据载入listview控件中
    第一个Directx程序
    edit编辑框只能输入数字和一个小数点
    (原创) 一个通用的C++ 消息总线框架
    springboot 集成logback 及配置,日志格式,重复打印配置
    算法09未排序数组中累加和为给定值的最长子数组长度
    算法12猫狗队列
    算法06由两个栈组成的队列
  • 原文地址:https://www.cnblogs.com/cxhscst2/p/7630291.html
Copyright © 2011-2022 走看看