[JZOJ5936]【NOIP2018模拟10.29】逛公园

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hzj1054689699/article/details/83513688

Description

策策同学特别喜欢逛公园,公园可以看做有n个景点的序列,每个景点会给策策带来di 的愉悦度,策策初始有x0 的愉悦度,然而愉悦度也是有上限的,他在每个景点的愉悦度上限为Li ,策策想要从 l 到 r这一段景点中选择一段景点参观(从这一段的左端点逛到这一段的右端点),策策想知道他最终的愉悦度的最大值是多少,你能帮帮他吗?(区间可以为空,也就是说答案最小为x0 )
n,di,q<=40000,Li<=1000000

Solution

一开始还看错题了…

我们设 F ( l , r , x ) F(l,r,x) 表示从l开始走,走到r,初始值为x的最终愉悦度。
那么题目就是求 max ( F ( u , v , x ) ) , [ u , v ] [ l , r ] \max\left(F(u,v,x)\right),[u,v]\in[l,r]

显然 F ( l , r , x ) F(l,r,x) 对于x是单调的,即当 x 1 x 2 , F ( l , r , x 1 ) F ( l , r , x 2 ) x_1\geq x_2,F(l,r,x_1)\geq F(l,r,x_2)
再设
G ( l , r ) = F ( l , r , I N F ) G(l,r)=F(l,r,INF) ,即从第一个开始就被卡住上限。
S ( l , r ) = i = l r d i S(l,r)=\sum\limits_{i=l}^{r}d_i

那么有结论, F ( l , r , x ) = min ( G ( l , r ) , x + S ( l , r ) ) F(l,r,x)=\min(G(l,r),x+S(l,r))
相当于是分中途是否有卡住上限来讨论。
容易让人疑惑的是,如果第一个没被卡住,后面被卡住了,这种情况算不到。
其实不会,第一个没被卡住,后面都会被卡住,那么如果第一个被卡住,后面肯定更大,更会被卡住,因此计算结果是一样的。

有了这个结论,我们考虑分块。
对于每一个询问,两边的散块显然可以暴力计算,考虑中间的整块。

对于第j块,设它的左右边界分别为 L j , R j L_j,R_j

考虑计算左右端点都在第j块中的子区间贡献。

显然,对于两个子区间 [ u 1 , v 1 ] , [ u 2 , v 2 ] [u_1,v_1],[u_2,v_2] ,若 S ( u 1 , v 1 ) &gt; S ( u 2 , v 2 ) S(u_1,v_1)&gt;S(u_2,v_2) G ( u 1 , v 1 ) &gt; G ( u 2 , v 2 ) G(u_1,v_1)&gt;G(u_2,v_2) ,那 [ u 2 , v 2 ] [u_2,v_2] 就没用了

我们将这n个子区间( n 2 {\sqrt n}^2 )拉出来按照S排序,类似单调栈维护,这样G就是递减的
那么要求 m i n ( S + x , G ) min(S+x,G) 的最大值,直接二分G这条线和S这条线的交点(S第一个大于G的位置)就行了。

考虑跨块的情况
假设我们已经知道了第j-1块能向后贡献的最大值为C(即第j块初始开头最大是多少)

我们现在需要知道的是,结尾在第j块中的最大值,以及第j块能贡献给下一块的最大值C是多少

那么我们可以维护一个前缀单调栈和后缀单调栈,类似上面的,对于前缀单调栈,我们想找到 m i n ( S + C , G ) min(S+C,G) 的最大值,同样二分。

对于对下一块的贡献,要么是以C为初始走过整一块,要么是从这块中某个位置开始以x为初始,要么直接以x为初始。第一第三种情况直接计算,第二种情况同样在后缀单调栈中二分,最后三者取最大值就是对下一块的最大贡献。

时间复杂度 O ( n n log n + q n log n ) O(n\sqrt n\log n+q\sqrt n\log n)
如果改用桶排,则可以优化到 O ( n n + q n log n ) O(n\sqrt n+q\sqrt n\log n)

根据平衡规划的思想,如果把块大小开到 n log n \sqrt {n\log n} ,那么复杂度可以到 O ( ( n + q ) n log n ) O((n+q)\sqrt{n\log n})
没写不知道。。。

反正直接第一种也跑的贼快

Code

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 40005
#define M 215
using namespace std;
int fr[N],q,n,a[N],n1,lim[N],sum[N],g[M][M][M],le[M],R,lp[M],ls[M];
struct node
{
	int x,y;
	friend bool operator <(node x,node y)
	{
		return (x.y<y.y)||(x.y==y.y&&x.x<y.x);	
	}
}d[M*M],st[M][M*M],pre[M][M],sub[M][M];
int get(int v,node *a,int len)
{
	int l=1,r=len;
	while(l+1<r)
	{
		int mid=(l+r)>>1;	
		if(a[mid].y+v>a[mid].x) r=mid;
		else l=mid;
	}
	int ans=min(a[r].y+v,a[r].x);
	if(r>1) ans=max(ans,min(a[r-1].y+v,a[r-1].x));
	if(r>2) ans=max(ans,min(a[r-2].y+v,a[r-2].x));
	return ans;
}
int main()
{
	cin>>n>>q;
	fo(i,1,n) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
	fo(i,1,n) scanf("%d",&lim[i]);
	R=sqrt(n);
	int cnt=0;
	n1=1;
	fo(i,1,n)
	{
		cnt++;
		if(cnt>R) fr[i]=++n1,cnt=1;
		else fr[i]=n1;
	}
	fo(i,1,n1)
	{
		int l=(i-1)*R+1,r=min(i*R,n),top=0,num=0;
		fo(j,l,r)
		{
			fo(k,j,r)
			{
				g[i][j-l][k-l]=(k==j)?lim[k]:min(g[i][j-l][k-l-1]+a[k],lim[k]);
				d[++num].x=g[i][j-l][k-l];
				d[num].y=sum[k]-sum[j-1];
			}
		}
		sort(d+1,d+num+1);
		fo(j,1,num)
		{
			while(top&&st[i][top].x<=d[j].x) top--;
			st[i][++top]=d[j];
		}
		le[i]=top;
		top=0;
		fo(j,l,r)
		{
			while(top&&pre[i][top].x<=g[i][0][j-l]) --top;
			pre[i][++top]=(node){g[i][0][j-l],sum[j]-sum[l-1]};
		}
		lp[i]=top;
		top=0;
		fod(j,r,l)
		{
			while(top&&sub[i][top].x<=g[i][j-l][r-l]) --top;
			sub[i][++top]=(node){g[i][j-l][r-l],sum[r]-sum[j-1]};
		}
		ls[i]=top;
	}
	fo(i,1,q)
	{
		int x,y,z,ans=0;
		scanf("%d%d%d",&x,&y,&z);
		if(fr[x]==fr[y]) 
		{
			int v=z;
			fo(j,x,y) v=max(min(lim[j],v+a[j]),z),ans=max(ans,v);
			printf("%d\n",ans);
		}
		else
		{
			int v=z;
			while(fr[x]==fr[x-1]) v=max(min(lim[x],v+a[x]),z),ans=max(ans,v),x++;
			fo(j,fr[x],fr[y]-1)
			{
				int lx=(j-1)*R+1,rx=min(n,j*R);
				ans=max(ans,max(get(v,pre[j],lp[j]),get(z,st[j],le[j])));
				v=max(max(z,min(v+sum[rx]-sum[lx-1],g[j][0][rx-lx])),get(z,sub[j],ls[j]));
			}
			x=(fr[y]-1)*R+1;
			fo(j,x,y) v=max(min(lim[j],v+a[j]),z),ans=max(ans,v);
			printf("%d\n",ans);
		}
	}
}

猜你喜欢

转载自blog.csdn.net/hzj1054689699/article/details/83513688