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(q∗n)
- 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) 表示 iii 到 jjj 的 DDD 之和
-
发现两个重要的性质(这题最关键的地方)
- 1.对于 a>=ba>=ba>=b , F(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>=G2 和 S1>=S2S1>=S2S1>=S2
- 那么第二个子串是一定不会取到的(由性质二得到)
- 于是考虑分块
-
先考虑块内的贡献
- 每块大小为 n\sqrt{n}n ,子串个数就是 O(n)O(n)O(n) 个
- 我们可以先将每一块中子串的 GGG 和 SSS 预处理出来
- 根据推论,用单调栈将没用的子串扔掉
- 那么剩下的序列就是个 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(n∗n+q∗n∗logn)
- (code中的 SSS 和 GGG 的单调性和题解中的相反,本质相同)
(结合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);
}
}

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

被折叠的 条评论
为什么被折叠?



