zoukankan      html  css  js  c++  java
  • [bzoj4540][Hnoi2016]序列——单调栈+莫队+RMQ

    题目大意:

    给定一个序列,每次询问一个区间[L,R]所有子区间的最小值之和。

    思路:

    考虑莫队如何转移,新增一个端点R,则增加的区间为[L...R-1,R],考虑这些区间新贡献的最小值。
    我们把从R开始向左单调下降的序列给求出来,不难发现最小值是由区间内包含的最靠左一个在单调下降序列里的元素的值所决定的。
    于是我们利用单调栈求出每一个元素前面第一个小于它的元素(pre_i),并求出以这个元素结尾的所有区间的最小值的和(f_i),不难发现(f_i=f_{pre_i}+(i-pre_i) imes a_i),递推求出来(f)数组之后我们要求新增加一个点的贡献,只需要找出区间内最小的点的位置(m),对于左端点在m之前的,最小值都是(a_m),区间左端点在([m+1,R])的部分我们可以用(f_i-f_m)得到,这样就可以用RMQ(O(1))转移了。
    但是这样还不够优秀,上面的做法提示我们,如果要求(sum_{i=L}^{R}min(a_i..a_R))这个东西,可以用RMQ求出最小值的位置之后再利用(f)数组相减来解决。
    考虑对于一个区间[L,R],最小值的位置为(m),那么显然对于([m+1,R])区间的所有点当它们为右端点时前缀最小值为(a_m),所以通过(sum_{i=m+1}^{R}(f_i-f_m))可以得到左右端点都在[m+1,R]的子区间的权值和,同理可得左右端点都在[L,m-1]的子区间的权值和,对于包含m点的区间可以直接通过((m-L+1) imes (R-m+1) imes a_m)计算出。
    于是得到了这一题较优秀的做法,时间复杂度(O(nlog n +q))

    #include<bits/stdc++.h>
    
    #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
    #define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
    #define MREP(i,x) for(int i=beg[x],v;v=to[i],i;i=las[i])
    #define debug(x) cout<<#x<<"="<<x<<endl
    #define pii pair<ll,int>
    #define fi first
    #define se second
    #define mk make_pair
    #define pb push_back
    typedef long long ll;
    
    using namespace std;
    
    void File(){
    	freopen("bzoj4540.in","r",stdin);
    	freopen("bzoj4540.out","w",stdout);
    }
    
    template<typename T>void read(T &_){
    	T __=0,mul=1; char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-')mul=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    	_=__*mul;
    }
    
    const int maxn=1e5+10;
    int n,m,Log[maxn];
    ll a[maxn],f1[maxn],g1[maxn],f2[maxn],g2[maxn];
    pii st[maxn][21];
    
    int query(int l,int r){
    	int d=Log[r-l+1];
    	return min(st[l][d],st[r-(1<<d)+1][d]).se;
    }
    
    void init(){
    	read(n); read(m);
    	REP(i,1,n)read(a[i]);
    
    	REP(i,2,n)Log[i]=Log[i/2]+1;
    	REP(i,1,n)st[i][0]=mk(a[i],i);
    	REP(j,1,20)REP(i,1,n-(1<<j)+1)
    		st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    
    	int tp;
    	ll s[maxn];
    
    	tp=0;
    	REP(i,1,n){
    		while(tp && a[s[tp]]>a[i])--tp;
    		f1[i]=f1[s[tp]]+(i-s[tp])*a[i];
    		s[++tp]=i; g1[i]=f1[i]+g1[i-1];
    	}
    	s[tp=0]=n+1;
    	DREP(i,n,1){
    		while(tp && a[s[tp]]>a[i])--tp;
    		f2[i]=f2[s[tp]]+(s[tp]-i)*a[i];
    		s[++tp]=i; g2[i]=f2[i]+g2[i+1];
    	}
    }
    
    void work(){
    	int l,r,p;
    	ll ans;
    	REP(i,1,m){
    		read(l); read(r);
    		p=query(l,r);
    		ans=a[p]*(p-l+1)*(r-p+1);
    		ans+=g1[r]-g1[p]-(r-p)*f1[p];
    		ans+=g2[l]-g2[p]-(p-l)*f2[p];
    		printf("%lld
    ",ans);
    	}
    }
    
    int main(){
    	File();
    	init();
    	work();
    	return 0;
    }
    
    
  • 相关阅读:
    git push 时报错 fatal: unable to access 'https://lisir2@github.com/lisir2/react-study.git/': The requested URL returned error: 403
    uniapp请求封装(适合小白,简单易懂,直接复制代码就可以用,像我一般懒得写 就直接复制改改就ok了)
    35. Binary Tree Level Order Traversal && Binary Tree Level Order Traversal II
    34. Convert Sorted List to Binary Search Tree && Convert Sorted Array to Binary Search Tree
    33. Minimum Depth of Binary Tree && Balanced Binary Tree && Maximum Depth of Binary Tree
    32. Path Sum && Path Sum II
    31. Flatten Binary Tree to Linked List
    30. Distinct Subsequences
    29. Populating Next Right Pointers in Each Node && Populating Next Right Pointers in Each Node II
    28. Triangle && Pascal's Triangle && Pascal's Triangle II
  • 原文地址:https://www.cnblogs.com/ylsoi/p/10090222.html
Copyright © 2011-2022 走看看