JZOJ-senior-5936. 【NOIP2018模拟10.29】逛公园

探讨在有限愉悦度上限下,如何在一系列景点中选择连续段落以最大化最终愉悦度的问题。通过预处理、分块和二分查找等算法优化,实现高效解答策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Time Limits: 2000 ms Memory Limits: 524288 KB

Description

策策由于在noip2017考试当天去逛公园了,没能出现在考场上,转眼到了noip2018,策策的公园也悄然转变,策策能否克服诱惑,成功坐在考场上呢?

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

Input

第一行两个数n,q 表示景点序列长度 和 询问个数
第二行n个数 表示di
第三行n个数 表示li
接下来q行,每行3个数:
表示 l ,r ,x0
下标均从1开始

Output

共q行,每行1个数表示愉悦度的最大值

Sample Input

6 3
0 5 3 2 0 4
8 10 8 1 9 9
1 3 9
2 6 3
3 4 0

Sample Output

10
8
3

样例说明
询问1 初始愉悦度9 只逛第2个公园 9+5=14 大于l2 ans=10
询问2 初始愉悦度3 从2逛到3 3+5+3=11 大于l3 ans=8
询问3 初始愉悦度0 只逛第3个公园 ans=3

Data Constraint

在这里插入图片描述

Solution

  • 50%:
  • 对于每个点,要是走它能使答案更优,那么走,否则就以初始值从下一个点开始走
  • 时间复杂度 O(q∗n)O(q*n)O(qn)
  • 100%:
  • 定义一些东西:
  • F(i,j,x0)F(i,j,x_0)F(i,j,x0) 表示以初始值 x0x0x0 经过 lll 到达 rrr 后的答案
  • G(i,j)=F(i,j,inf)G(i,j)=F(i,j,inf)G(i,j)=F(i,j,inf) (inf=inf=inf=+∞)
  • S(i,j)S(i,j)S(i,j) 表示 iiijjjDDD 之和
  • 发现两个重要的性质(这题最关键的地方)
  • 1.对于 a>=ba>=ba>=bF(i,j,a)>=F(i,j,b)F(i,j,a)>=F(i,j,b)F(i,j,a)>=F(i,j,b)
  • 2.F(i,j,x0)=min(G(i,j),x0+S(i,j))F(i,j,x_0)=min(G(i,j),x_0+S(i,j))F(i,j,x0)=min(G(i,j),x0+S(i,j))
  • 推论
  • 对于询问的 l,rl,rl,r ,如果两个子串都在 [l,r][l,r][l,r] 中,且 G1>=G2G1>=G2G1>=G2S1>=S2S1>=S2S1>=S2
  • 那么第二个子串是一定不会取到的(由性质二得到)
  • 于是考虑分块
  • 先考虑块内的贡献
  • 每块大小为 n\sqrt{n}n ,子串个数就是 O(n)O(n)O(n)
  • 我们可以先将每一块中子串的 GGGSSS 预处理出来
  • 根据推论,用单调栈将没用的子串扔掉
  • 那么剩下的序列就是个 GGG 不断减小,SSS 不断增大的序列
  • 对于每次的询问 x0x_0x0 ,最大的点一定是 minminmin 函数中间的某处
  • minminmin 函数前半部分递减,后半部分递增,看成两条直线,则相交处将有最大值)
  • 二分就可以得到最大的答案啦
  • 再考虑块间的贡献
  • 两种情况:
  • 1.当前块开始,后面某块结束
  • 2.前面某块开始,当前块结束
  • 参考 50% 的暴力策略,用 YYY 代表上一个块给这一个块带来的贡献,当然是越大越好
  • (每个块中的前缀和后缀可以在处理子串的同时预处理)
  • 利用前缀和 YYY ,可采用和块内贡献相同的二分方法计算答案
  • 利用后缀,同理也可计算这一块可以提供给下一块的 YYY
  • 分三种情况讨论
  • 1.从上一个 YYY 走满整块
  • 2.从某个后缀走到末尾
  • 3.直接取 x0x0x0
  • 三者的最大值就是 YYY
  • 时间复杂度 O(n∗n+q∗n∗logn)O(n*\sqrt{n}+q*\sqrt{n}*logn)O(nn+qnlogn)
  • (code中的 SSSGGG 的单调性和题解中的相反,本质相同)
  • (结合code中的注释体验++)

Code

#include<algorithm>
#include<cstdio>
#include<cmath>

#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,a,b) for(int i=a;i>=b;--i)

using namespace std;

const int N=4e4+5,M=2e2+5,inf=1e9;
int n,q,len,tot;
int a[N],s[N],up[N],be[N];
struct node{int g,s;}d[N],d1[M],d2[M];
node c[M][N],all[M],pre[M][M],suf[M][M];

bool cmp(node x,node y)
{
	return x.g<y.g||x.g==y.g&&x.s>y.s;
}

void get(node *d,int t,node *c)//stack g+ s-
{
	sort(d+1,d+1+t,cmp);
	int tp=0;
	fo(i,1,t) if(d[i].g>d[i-1].g)
	{
		while(tp&&c[tp].s<d[i].s) --tp;
		c[++tp]=d[i];
	}
	c[0].g=tp;
}

void prepare()
{
	fo(k,1,tot)
	{
		int st=(k-1)*len+1,en=min(k*len,n);
		int t=0,t1=0,t2=0;
		fo(i,st,en)
		{
			int now=inf;
			fo(j,i,en)
			{
				now=min(now+a[j],up[j]);
				d[++t]=(node){now,s[j]-s[i-1]};//each
				if(i==st) d1[++t1]=d[t];//prefix
				if(j==en) d2[++t2]=d[t];//suffix
			}
			if(i==st) all[k]=d[t];//a whole block
		}
		get(d,t,c[k]);
		get(d1,t1,pre[k]);
		get(d2,t2,suf[k]);
	}
}

int solve(node *b,int x0)
{
	int l=1,r=b[0].g;
	while(r-l>1)
	{
		int mid=(l+r)>>1;
		if(b[mid].g<x0+b[mid].s) l=mid;
			else r=mid;
	}
	return max(min(b[l].g,x0+b[l].s),min(b[r].g,x0+b[r].s));
}

int main()
{
	freopen("park.in","r",stdin);
	freopen("park.out","w",stdout);
	scanf("%d%d",&n,&q);
	len=sqrt(n),tot=(n-1)/len+1;
	fo(i,1,n) scanf("%d",&a[i]),s[i]=s[i-1]+a[i];
	fo(i,1,n) scanf("%d",&up[i]),be[i]=(i-1)/len+1;
	
	prepare();
	
	while(q--)
	{
		int l,r,x;
		scanf("%d%d%d",&l,&r,&x);
		int st=be[l],en=be[r],ans=x,y=x;
		fo(i,l,min(r,st*len))
		{
			y=max(x,min(y+a[i],up[i]));
			ans=max(ans,y);
		}
		fo(i,st+1,en-1)
		{
			ans=max(ans,solve(c[i],x));//begin and end in block i
			ans=max(ans,solve(pre[i],y));//end in block i
			//update new y
			y=min(all[i].g,y+all[i].s);//1
			y=max(solve(suf[i],x)/*2*/,max(x,y)/*3*/);
		}
		if(st<en)
		{
			fo(i,max(l,(en-1)*len+1),r)
			{
				y=max(x,min(y+a[i],up[i]));
				ans=max(ans,y);
			}
		}
		printf("%d\n",ans);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值