zoukankan      html  css  js  c++  java
  • bzoj4540: [Hnoi2016]序列

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4540

    思路:又是莫队....

    我们发现左右端点移动时,只会增加或删除某个点开头或结尾的区间

    先考虑右端点从r移动到r+1

    令p为[l,r]中最小值的位置

    那么它会对新加的区间中的p-l+1个区间产生a[p]的贡献

    另一些左端点在[p+1,r],右端点是r+1的区间怎么统计呢?

    首先用单调栈求出l[i],r[i]表示向左第一个小于a[i]的数的位置,向右第一个小于等于a[i]的数的位置

    (注意:一个是小于,一个是小于等于,这样是防止重复计算)

    预处理出两个类似前缀和的数组sl[i]和sr[i]

    其中sl[i]=sl[l[i]]+(i-l[i])*a[i],sr同理

    表示以这个为结束的答案的前缀和

    左端点在l[i]及以前的答案就是sl[l[i]],左端点在[l[i],i]之间的最小值肯定是a[i],所以答案加上(i-l[i])*a[i]


    那么左端点在[p+1,r],右端点是r+1的区间对答案的贡献就是sl[r+1]-sl[p]


    那么移动后,答案加上(p-l+1)*a[i]+sl[r]-sl[p]即可

    左端点移动及删除类似

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    typedef long long ll;
    const int maxn=100010,maxk=22,inf=1e9;
    using namespace std;
    int n,Q,sz,top,a[maxn],st[maxn][maxk],pw[maxk],stk[maxn],L[maxn],R[maxn],bel[maxn],lg[maxn];ll sl[maxn],sr[maxn],now,ans[maxn];
    struct quer{int l,r,id;}q[maxn];
    bool operator <(quer a,quer b){return bel[a.l]==bel[b.l]?a.r<b.r:bel[a.l]<bel[b.l];}
    int getmin(int x,int y){return a[x]<a[y]?x:y;}
    int calc(int x,int y){
    	if (x>y) swap(x,y);
    	int l=lg[y-x+1];
    	//printf("x=%d y=%d log=%d minpos%d
    ",x,y,l,getmin(st[x][l],st[y-pw[l]+1][l]));
    	return getmin(st[x][l],st[y-pw[l]+1][l]);
    }
    
    void modifyl(int l,int r,int op){
    	int p=calc(l,r);
    	ll tmp=1ll*a[p]*(r-p+1)+sr[l]-sr[p];
    	now+=op*tmp;
    }
    
    void modifyr(int l,int r,int op){
    	int p=calc(l,r);
    	ll tmp=1ll*a[p]*(p-l+1)+sl[r]-sl[p];
    	now+=op*tmp;
    }
    
    void work(){
    	sort(q+1,q+1+Q);now=a[1];
    	for (int l=1,r=1,i=1;i<=Q;i++){
    		//printf("i=%d
    ",i);
    		for (;r<q[i].r;r++) modifyr(l,r+1,1);
    		for (;l>q[i].l;l--) modifyl(l-1,r,1);
    		for (;r>q[i].r;r--) modifyr(l,r,-1);
    		for (;l<q[i].l;l++) modifyl(l,r,-1);
    		ans[q[i].id]=now;
    	}
    	for (int i=1;i<=Q;i++) printf("%lld
    ",ans[i]);
    }
    
    int main(){
    	//freopen("sequence1.in","r",stdin);freopen("sequence.out","w",stdout);
    	scanf("%d%d",&n,&Q),sz=sqrt(n),lg[0]=-1;
    	pw[0]=1;for (int i=1;i<=18;i++) pw[i]=pw[i-1]<<1;
    	for (int i=1;i<=n;i++) lg[i]=lg[i>>1]+1,bel[i]=(i-1)/sz+1;
    	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    	for (int i=1;i<=n;i++) st[i][0]=i;
    	for (int j=1;j<=18;j++)
    		for (int i=1;i<=n;i++){
    			st[i][j]=st[i][j-1];
    			if (i+pw[j-1]<=n) st[i][j]=getmin(st[i][j-1],st[i+pw[j-1]][j-1]);
    			//printf("%d ",st[i][j]);
    		}
    	for (int i=1;i<=n;i++){
    		while (top&&a[stk[top]]>=a[i]) R[stk[top--]]=i;
    		stk[++top]=i;
    	}
    	while (top) R[stk[top--]]=n+1;
    	for (int i=n;i;i--){
    		while (top&&a[stk[top]]>a[i]) L[stk[top--]]=i;
    		stk[++top]=i;
    	}
    	while (top) L[stk[top--]]=0;
    	
    	//for (int i=1;i<=n;i++) printf("i=%d L=%d R=%d
    ",i,L[i],R[i]);
    	for (int i=n;i;i--) sr[i]=sr[R[i]]+1ll*(R[i]-i)*a[i];
    	for (int i=1;i<=n;i++) sl[i]=sl[L[i]]+1ll*(i-L[i])*a[i];
    	//for (int i=1;i<=n;i++) printf("i=%d sl=%lld sr=%lld
    ",i,sl[i],sr[i]);
    	for (int i=1;i<=Q;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
    	work();
    	return 0;
    }
    /*
    5 5
    5 2 4 1 3
    1 5
    1 3
    2 4
    3 5
    2 5
    
    */



  • 相关阅读:
    来看看面试必问的HashMap,一次彻底帮你搞定HashMap源码
    深入浅出!springboot从入门到精通,实战开发全套教程!
    讲一讲Java的字符串常量池,看完你的思路就清晰了
    面向对象的这些核心技术,你掌握后包你面试无忧
    他凭借这70份PDF,3170页文件,成功斩获了含BATJ所有的offer
    springboot实战开发全套教程,让开发像搭积木一样简单!Github星标已上10W+!
    这行代码告诉你!为什么你地下城与勇士(DNF)的装备强化老是失败?
    精益求精!Spring Boot 知识点全面回顾,带你重新细读源码!
    太妙了!Spring boot 整合 Mybatis Druid,还能配置监控?
    putchar(".:-=+*#%@"[(int)(d * 5.0f)])
  • 原文地址:https://www.cnblogs.com/thythy/p/5493636.html
Copyright © 2011-2022 走看看