版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/HuangXinyue1017/article/details/83546935
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%:
- 对于每个点,要是走它能使答案更优,那么走,否则就以初始值从下一个点开始走
- 时间复杂度
- 100%:
- 定义一些东西:
- 表示以初始值 经过 到达 后的答案
- ( +∞)
- 表示 到 的 之和
-
发现两个重要的性质(这题最关键的地方)
- 1.对于 ,
- 2.
- 推论
- 对于询问的 ,如果两个子串都在 中,且 和
- 那么第二个子串是一定不会取到的(由性质二得到)
- 于是考虑分块
-
先考虑块内的贡献
- 每块大小为 ,子串个数就是 个
- 我们可以先将每一块中子串的 和 预处理出来
- 根据推论,用单调栈将没用的子串扔掉
- 那么剩下的序列就是个 不断减小, 不断增大的序列
- 对于每次的询问 ,最大的点一定是 函数中间的某处
- ( 函数前半部分递减,后半部分递增,看成两条直线,则相交处将有最大值)
- 二分就可以得到最大的答案啦
-
再考虑块间的贡献
- 两种情况:
- 1.当前块开始,后面某块结束
- 2.前面某块开始,当前块结束
- 参考 50% 的暴力策略,用 代表上一个块给这一个块带来的贡献,当然是越大越好
- (每个块中的前缀和后缀可以在处理子串的同时预处理)
- 利用前缀和 ,可采用和块内贡献相同的二分方法计算答案
- 利用后缀,同理也可计算这一块可以提供给下一块的
- 分三种情况讨论
- 1.从上一个 走满整块
- 2.从某个后缀走到末尾
- 3.直接取
- 三者的最大值就是
- 时间复杂度
- (code中的 和 的单调性和题解中的相反,本质相同)
(结合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);
}
}