[CF878E]Numbers on the blackboard

E - Numbers on the blackboard

最后的答案肯定为lir2pi×ai

然后这个p满足以下限制:

  • pi=0i=l

  • 1pipi1+1l<ir)

以下不特别考虑i=1

那么若ai>0,那么我们肯定是希望它的pi尽可能大也就是取pi1+1,即在i1合并到前面去之前,i就合并到i1

步骤一:那么先不考虑询问,我们要使得答案最大,肯定是从最右端开始向左合并,直到当前合并出来的值为负数,就停下,然后向前找,直到找到下一个为非负数的点,然后又从它开始向前合并

步骤二:那么最后就只剩下了一堆权值为负的点(也有可能只剩下一个非负的点),那么对于这些点我们希望它们的pi尽可能小,所以现在从左往右合并,那么每个点的pi都等于1

现在考虑能不能从左往右加点的进行步骤一,显然是可以的

假设我们已经处理好了当前已经加进来了的所有点最后缩成的负点,那么加入一个新点t,若它是负数,就不管,否则就将它与目前最后一个负点融合,它的贡献就是2sz×at,其中sz表示最后一个负点所代表的原来的点的数量

若这个负点又变成正的了,就继续向前融合

这样的话,我们就可以将询问离线,对所有询问按右端点从小到大排序,然后考虑怎么处理询问,即怎么进行步骤二

我们先加点加到r,这样就不用考虑右边界,接下来考虑l

找到l所处的点,那么对于这个点之后的所有点,它们都可以用上述步骤二的方式解决,即它们的pi都是1,对于l处于的这个点,将它拆成原来的样子,舍去l左边多余的原来的点,剩下的原来的点,因为都是这个点所代表的一个区间的点的右边部分,所以它们一定可以全部按照步骤一的方式合并

于是就做完了,撒花*★,°*:.☆( ̄▽ ̄)/$:*.°★*

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+5,MOD=1e9+7;
const ll INF=1e14+114514;
int n,q,a[N],ans[N];
struct node{
	int l,r,id;
	bool operator < (const node &other) const{
		return r<other.r;
	}
}cn[N];
int pos=1,st[N],top;
ll block[N],sumb[N];
ll p2[N],sum[N],inv2[N];
void merge(){
	if((1ll<<st[top]-st[top-1])>=INF||((1ll<<st[top]-st[top-1])>(INF-block[top-1])/block[top])) block[--top]=INF;
	else block[top-1]=block[top-1]+(block[top]<<st[top]-st[top-1]),--top;
}
void go(int goal){
	while(pos<=goal){
		block[++top]=a[pos],st[top]=pos;
		while(top>1&&block[top]>0) merge();
		if(block[top]>=INF) sumb[top]=sum[pos];
		else sumb[top]=((sumb[top-1]+block[top]%MOD)%MOD+MOD)%MOD;
		++pos;
	}
}
int ask(int l,int r){
	st[top+1]=r+1;
	int t=upper_bound(st+1,st+top+2,l)-st;
	return (((sumb[top]-sumb[t-1])%MOD+MOD)%MOD*2%MOD+((sum[st[t]-1]-sum[l-1])%MOD+MOD)%MOD*inv2[l]%MOD)%MOD;
}
int main(){
	scanf("%d%d",&n,&q);
	p2[0]=inv2[0]=1;
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		p2[i]=p2[i-1]*2%MOD,inv2[i]=inv2[i-1]*500000004%MOD;
		sum[i]=(sum[i-1]+a[i]*p2[i]%MOD)%MOD;
	}
	for(int i=1;i<=q;++i) scanf("%d%d",&cn[i].l,&cn[i].r),cn[i].id=i;
	sort(cn+1,cn+q+1);	
	for(int i=1;i<=q;++i) go(cn[i].r),ans[cn[i].id]=ask(cn[i].l,cn[i].r);
	for(int i=1;i<=q;++i) printf("%d\n",ans[i]);
 
 
	return 0;
}
posted @   LuoyuSitfitw  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示