zoukankan      html  css  js  c++  java
  • 题解-[HNOI2016]序列

    题解-[HNOI2016]序列

    [HNOI2016]序列

    给定 (n)(m) 以及序列 (a{n})。有 (m) 次询问,每次给定区间 ([l,r]in[1,n]),求

    [sum_{lle l'le r'le r}min_{i=l'}^{r'}a_i ]

    数据范围:(1le n,mle 10^5)(|a_i|le 10^9)


    蒟蒻要练习省选题,结果就遇到这道数据结构(好久没写数据结构题都忘光了)。结果正好遇到一道毒瘤题,于是蒟蒻来写篇题解。


    这题是静态离线,令人想到 ( exttt{ST}) 表和莫队——真的就是他们。

    将区间存下来排序,将左端点范围从小到大分 (sqrt n) 份,左端点按份排序,右端点奇偶波浪排序。

    friend int operator<(Moq x,Moq y){
    	if(cas[x.l]!=cas[y.l]) return x.l<y.l;
    	return (cas[x.l]&1)?x.r<y.r:x.r>y.r;
    }
    

    然后依次考虑排序后每一个区间询问,并通过上一个区间的答案递推,这就是莫队的思想


    然后看这题:

    如何通过上一个答案递推呢?需要先知道边界端点的贡献

    即给定区间 ([l,r])(l) 端点的贡献(=Ans[l,r]-Ans[l+1,r])

    比如下面给出一个序列 (a{n}(n=10))

    4 4 5 3 6 2 1 5 6 9
    

    给定区间 ([l,r]=[3,9]),即 (a_lsim a_r) 为:

    5 3 6 2 1 5 6
    

    所以左端点 ((l=3,a_l=5)) 的贡献应该为

    [sum_{j=l}^rminlimits_{k=l}^j a_k ]

    [min{5}+min{5,3}+min{5,3,6}+cdots+min{5,3,6,2,1,5,6} ]

    [=5+3+3+2+1+1+1 ]

    令人想起单调栈,然而不可能每次询问 (Theta(n)) 跑一遍。所以可以预处理:

    通过维护单调栈,求出对于每个 (a_i)(lw_i(lw_i<i)) 表示 (i) 左边第一个比 (a_i) 小的元素的下标;(rw_i(rw_i>i)) 表示 (i) 右边第一个比 (a_i) 小的元素的下标(如果左边不存在比 (a_i) 小的元素,(lw_i=0);如果右边不存在比 (a_i) 小的元素,(rw_i=n+1))。

    这样的话,就可以维护一个前缀和 (lsm_i) 后缀和 (rsm_i),其中

    [lsm_i=lsm_{lw_i}+a_i(i-lw_i),rsm_i=rsm_{rw_i}+a_i(rw_i-i) ]

    然后上面的 (5+3+3+2) 就可以通过后缀和相减得,求右端点贡献时则用前缀和相减

    因为对于区间 ([lw_i+1,i])([i,rw_i-1])(a_i) 为其最小元素。

    void Side(){
    	lw.rz(n+7),rw.rz(n+7);
    	a[0]=-inf;
    	q.clear(),q.pb(0);
    	for(int i=1;i<=n;i++){
    		while(q.size()&&a[q.back()]>=a[i]) q.pop_back();
    		lw[i]=q.back(),q.pb(i);
    	}
    	a[0]=0;
    	a[n+1]=-inf;
    	q.clear(),q.pb(n+1);
    	for(int i=n;i>=1;i--){
    		while(q.size()&&a[q.back()]>=a[i]) q.pop_back();
    		rw[i]=q.back(),q.pb(i);
    	}
    	a[n+1]=0;
    	lsm.rz(n+7),rsm.rz(n+7);
    	for(int i=1;i<=n;i++) lsm[i]=lsm[lw[i]]+(lng)(i-lw[i])*a[i];
    	for(int i=n;i>=1;i--) rsm[i]=rsm[rw[i]]+(lng)(rw[i]-i)*a[i];
    }
    

    最后一个问题:(1+1+1) 部分怎么办?

    因为右端点是随机的,所以如果直接把左端点的贡献当做 (rsm_l-rsm_r),必然不妥。

    考虑到假设区间 ([l,r])(a_p) 最小,那么必然

    [forall jin[p,r],left(minlimits_{k=l}^j a_k ight)=a_p ]

    所以算 (1+1+1) 部分可以通过维护静态区间最小值下标( exttt{ST}) 表)找到 (p),算出 (a_p(r-p+1))

    然后正因为 (a_p)([l,r]) 区间中最小的元素,所以 ([l,p-1]) 段的左端点贡献也自然是 (rsm_l-rsm_p)

    所以对于区间 ([l,r]),左端点贡献为

    [rsm_l-rsm_p+a_p(r-p+1)qquad [a_p=min_{i=l}^r a_i] ]

    右端点同理。

    然后相邻两个区间之间就可以逐步转移了。

    lng Mol(int l,int r){
    	int p=getmin(l,r);
    	return rsm[l]-rsm[p]+(lng)(r-p+1)*a[p];
    }
    lng Mor(int l,int r){
    	int p=getmin(l,r);
    	return lsm[r]-lsm[p]+(lng)(p-l+1)*a[p];
    }
    void runMo(){
    	int L=1,R=0;
    	for(int i=1;i<=m;i++){
    		while(L>qu[i].l) res+=Mol(--L,R);
    		while(R<qu[i].r) res+=Mor(L,++R);
    		while(L<qu[i].l) res-=Mol(L++,R);
    		while(R>qu[i].r) res-=Mor(L,R--);
    		ans[qu[i].I]=res;
    	}
    }
    

    时间复杂度 (Theta(msqrt n)),空间复杂度 (Theta(n+m))


    Code

    我这个蒟蒻垃圾真是傻傻讲不清楚,还是放代码吧(要开 ( exttt{long long}))。

    代码有点长,于是蒟蒻划分了一下。

    #include <bits/stdc++.h>
    using namespace std;
    
    //Start
    #define lng long long
    #define db double
    #define mk make_pair
    #define pb push_back
    #define fi first
    #define se second
    #define rz resize
    const int inf=0x3f3f3f3f;
    const lng INF=0x3f3f3f3f3f3f3f3f;
    
    //Data
    int n,m;
    vector<int> a;
    
    //Side
    vector<int> lw,rw,q;
    vector<lng> lsm,rsm;
    void Side(){
    	lw.rz(n+7),rw.rz(n+7);
    	a[0]=-inf;
    	q.clear(),q.pb(0);
    	for(int i=1;i<=n;i++){
    		while(q.size()&&a[q.back()]>=a[i]) q.pop_back();
    		lw[i]=q.back(),q.pb(i);
    	}
    	a[0]=0;
    	a[n+1]=-inf;
    	q.clear(),q.pb(n+1);
    	for(int i=n;i>=1;i--){
    		while(q.size()&&a[q.back()]>=a[i]) q.pop_back();
    		rw[i]=q.back(),q.pb(i);
    	}
    	a[n+1]=0;
    	lsm.rz(n+7),rsm.rz(n+7);
    	for(int i=1;i<=n;i++) lsm[i]=lsm[lw[i]]+(lng)(i-lw[i])*a[i];
    	for(int i=n;i>=1;i--) rsm[i]=rsm[rw[i]]+(lng)(rw[i]-i)*a[i];//中间递推也要注意爆int
    }
    
    //ST
    vector<int> lg;
    vector<vector<int> > st;
    int stcmp(int x,int y){
    	return a[x]<a[y]?x:y;
    }
    void buildst(){
    	lg.rz(n+7);
    	for(int i=2;i<=n;i++) lg[i]=lg[i-1]+((1<<(lg[i-1]+1))<=i?1:0); //lg[i]=(int)log(i)
    	st.rz(lg[n]+7);
    	for(int j=0;j<=lg[n];j++) st[j].rz(n+7);
    	for(int i=1;i<=n;i++) st[0][i]=i;
    	for(int j=1;j<=lg[n];j++)
    		for(int i=1;i<=n-(1<<(j-1));i++) st[j][i]=stcmp(st[j-1][i],st[j-1][i+(1<<(j-1))]);
    }
    int getmin(int l,int r){
    	int len=lg[r-l+1];
    	return stcmp(st[len][l],st[len][r-(1<<len)+1]);
    }
    
    //Moq
    int sq;
    lng res;
    vector<int> cas;
    vector<lng> ans;
    struct Moq{
    	int l,r,I;
    	friend int operator<(Moq x,Moq y){
    		if(cas[x.l]!=cas[y.l]) return x.l<y.l;
    		return (cas[x.l]&1)?x.r<y.r:x.r>y.r;
    	}
    };
    vector<Moq> qu;
    void buildMo(){
    	cas.rz(n+7),qu.rz(m+1);
    	sq=sqrt(n);
    	for(int i=1;i<=m;i++) scanf("%d%d",&qu[i].l,&qu[i].r),qu[i].I=i;
    	for(int i=1;i<=n;i++) cas[i]=(i-1)/sq+1;
    	sort(qu.begin(),qu.end());
    }
    lng Mol(int l,int r){
    	int p=getmin(l,r);
    	return rsm[l]-rsm[p]+(lng)(r-p+1)*a[p];
    }
    lng Mor(int l,int r){
    	int p=getmin(l,r);
    	return lsm[r]-lsm[p]+(lng)(p-l+1)*a[p];
    }
    void runMo(){
    	int L=1,R=0;
    	for(int i=1;i<=m;i++){
    		while(L>qu[i].l) res+=Mol(--L,R);
    		while(R<qu[i].r) res+=Mor(L,++R);
    		while(L<qu[i].l) res-=Mol(L++,R);
    		while(R>qu[i].r) res-=Mor(L,R--);
    		ans[qu[i].I]=res;
    	}
    }
    
    //Main
    int main(){
    	scanf("%d%d",&n,&m);
    	a.rz(n+7);
    	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    	ans.rz(m+7);
    	Side(),buildst(),buildMo(),runMo();
    	for(int i=1;i<=m;i++)
    		printf("%lld
    ",ans[i]);
    	return 0;
    }
    

    祝大家学习愉快!

  • 相关阅读:
    React的状态管理工具
    Ant Design
    Mac 10.12安装专业抓包工具Wireshark
    Mac 10.12安装飞鸽传书IPMessager
    Mac 10.12安装XMind
    Mac 10.12安装SVN工具SmartSVM 7.6
    Mac 10.12安装Windows远程桌面工具Microsoft Remote Desktop
    Mac 10.12安装Office 2011
    Mac 10.12安装迅雷2.7.2
    Mac 10.12安装虚拟机软件VMware Fusion 12
  • 原文地址:https://www.cnblogs.com/George1123/p/12634706.html
Copyright © 2011-2022 走看看