ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【CF878E】Numbers on the blackboard(贪心)

2021-09-08 21:32:17  阅读:179  来源: 互联网

标签:CF878E blackboard 元素 long Numbers 权值 INF 正数 define


题目链接

  • 给定一个长度为 \(n\) 的序列 \(a_{1\sim n}\)。
  • \(q\) 次询问,每次取出序列中的一个区间 \([l,r]\) 内的元素进行操作。
  • 一次操作可以合并相邻两个元素(假设依次为 \(x\) 和 \(y\)),得到一个值为 \(x+2y\) 的新元素。对于每次询问,求出求最终能得到的元素的最大值。
  • \(1\le n,q\le10^5\),\(|a_i|\le10^9\),答案向 \(10^9+7\) 取模

巧妙的贪心策略

设第 \(i\) 个元素对答案的贡献系数为 \(2^{k_i}\)。

除开头元素外,第 \(i\) 个元素都至少被合并一次,且一次合并之后就会始终跟随着第 \(i-1\) 个元素,也就是说 \(1\le k_i\le k_{i-1}+1\)。

而我们肯定想让正数的系数尽可能大,因此如果一个数是正数,我们肯定贪心地让 \(k_i\) 取满 \(k_{i-1}+1\)。

考虑把 \(k_i\) 连续递增的一段元素称作一个块。

则正数肯定会被添到上一个块的末尾,负数则会自成一个新块。

但要,如果正数被添到上一个块末尾后上一个块的权值变成了正数,我们需要继续把这个块看成一个元素向前合并。具体实现可以用一个栈来维护所有的块,要保证除第一个块之外的所有块权值为负。

注意用于判断正负的权值是不能取模的,而如果无脑维护可能会爆 long long。实际上,当权值非常大时,肯定能一路向前合并完,可以统一设为 INF。

离线询问

询问可以离线,按右端点 \(r\) 排序,这样我们就可以维护好加入 \(1\sim r\) 中所有元素的局面,然后每次 lower_bound 找出第 \(l\) 个元素所在的块(假设为 \(x\))。

\(l\) 到所在块结尾 \(ed\) 中第 \(i\) 个元素贡献系数为 \(2^{i-l}\),只需用 \(\sum_{i=l}^{ed}a_i\times 2^i\) 除以 \(2^l\),这可以预处理一个前缀和差分求解。

而第 \(x+1\) 个块开始的答案就是这些块的权值和,同样维护前 \(i\) 个块的权值和每次差分一下即可。

注意除开头元素外,每个元素至少合并一次,\(k_i\ge 1\)。

代码:\(O(nlogn)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 100000
#define X 1000000007
#define I2 500000004
#define LL long long
#define INF (LL)1e18
using namespace std;
int n,a[N+5],ans[N+5];struct Q {int p,l,r;I bool operator < (Cn Q& o) Cn {return r<o.r;}}q[N+5];
int pw[N+5],ipw[N+5],S[N+5],V[N+5],St[N+5],F[N+5];LL G[N+5];
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int ff,OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0,ff=1;W(!isdigit(oc=tc())) ff=oc^'-'?1:-1;W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));x*=ff;}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');}
}using namespace FastIO;
int main()
{
	RI Qt,i;for(read(n,Qt),i=1;i<=n;++i) read(a[i]);for(i=1;i<=Qt;++i) read(q[i].l,q[i].r),q[i].p=i;
	for(pw[0]=ipw[0]=i=1;i<=n;++i) pw[i]=2LL*pw[i-1]%X,ipw[i]=1LL*I2*ipw[i-1]%X,S[i]=(S[i-1]+1LL*pw[i]*(a[i]+X))%X;//预处理
	RI x,j=1,T=0;for(sort(q+1,q+Qt+1),i=1;i<=n;++i)//将询问离线,枚举右端点
	{
		St[++T]=i,F[T]=(a[i]+X)%X,G[T]=a[i];//新开一个块
		W(T>1&&G[T]>=0) x=St[T]-St[T-1],F[T-1]=(F[T-1]+1LL*F[T]*pw[x])%X,G[T-1]=G[T-1]+1.0*G[T]*pw[x]>=INF?INF:G[T-1]+G[T]*pw[x],--T;//若最后一个块权值非负,向前合并
		V[T]=(V[T-1]+F[T])%X,St[T+1]=i+1;//V记录前i块元素之和
		W(j<=Qt&&q[j].r==i) x=upper_bound(St+1,St+T+1,q[j].l)-St,ans[q[j].p]=(1LL*ipw[q[j].l]*(S[St[x]-1]-S[q[j].l-1]+X)+2LL*(V[T]-V[x-1]+X))%X,++j;//找到l所在块求解
	}
	for(i=1;i<=Qt;++i) writeln(ans[i]);return clear(),0;
}

标签:CF878E,blackboard,元素,long,Numbers,权值,INF,正数,define
来源: https://www.cnblogs.com/chenxiaoran666/p/CF878E.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有