题目大意
给定一个
n
n
n个点的序列,每个点有
a
i
a_i
ai和
l
i
l_i
li两个信息,
a
i
a_i
ai表示可以获得的贡献,
l
i
l_i
li表示获得贡献的上限.
有
q
q
q个询问,每个询问
l
,
r
,
x
0
l,r,x0
l,r,x0,表示从
l
l
l到
r
r
r这个区间,选择一个子区间,从子区间的左端走到右端,获得的贡献最大.(子区间可以为空)
要求你输出最大可以获得的贡献
数据范围
对于
100
%
100\%
100%的数据
n
,
q
<
=
40000
、
a
i
<
=
10000
、
l
i
<
=
1000000
n,q<=40000、a_i<=10000、l_i<=1000000
n,q<=40000、ai<=10000、li<=1000000
Solution
原题bzoj2122
对于这种看着很难做的题首先考虑性质,先寻找性质再寻找方法.
设
F
(
i
,
j
,
x
0
)
F(i,j,x0)
F(i,j,x0)表示以
x
0
x0
x0为初始值,从
i
i
i走到
j
j
j的答案,有两个性质
1.若
a
>
=
b
a>=b
a>=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.设
G
(
i
,
j
)
G(i,j)
G(i,j)为
F
(
i
,
j
,
I
N
F
)
F(i,j,INF)
F(i,j,INF),
S
(
i
,
j
)
S(i,j)
S(i,j)为
i
i
i到
j
j
j的贡献之和,则
F
(
i
,
j
,
x
0
)
=
m
i
n
(
G
(
i
,
j
)
,
S
(
i
,
j
)
+
x
0
)
F(i,j,x0)=min(G(i,j),S(i,j) + x0)
F(i,j,x0)=min(G(i,j),S(i,j)+x0)
第一个性质显然,第二个性质分两种情况来讨论即可(没被卡住,被卡住).
推论:
对于一个询问
l
.
.
r
l..r
l..r,若两个子串都被包含在
l
l
l,
r
r
r内,且
G
1
>
=
G
2
G1>=G2
G1>=G2,并且
S
1
>
=
S
2
S1>=S2
S1>=S2,那么第二个子串就是无效的,由性质二可知.
于是可以考虑分块.
先预处理每个块的信息,包括
G
,
S
G,S
G,S,前缀的
G
,
S
G,S
G,S,后缀的
G
,
S
G,S
G,S.(排序+单调栈)
考虑计算贡献
块内贡献,直接二分计算
块间贡献:
维护一个Y值,代表上个块能给当前块带来的最大贡献.
贡献的计算:把Y值跟前缀一起二分计算.
Y值的维护:
共三种情况
1.走完整个块(预处理的时候维护一下)
2.从某个后缀开始(二分计算)
3.x0
三者最大值即是Y
复杂度
O
(
n
∗
n
+
q
∗
n
∗
l
o
g
n
)
O(n*\sqrt{n}+q*\sqrt{n} \ *log_n)
O(n∗n+q∗n ∗logn)
可能常数有点大.
Codes
#include <cstdio>
#include <climits>
#include <cmath>
#include <algorithm>
#include <cstring>
#define F f[num]
using namespace std;
const int N = 4e4 + 5,M = 2e2 + 5,INF = 0x3f3f3f3f;
struct node
{
int g,s;
}b[N],sta[N],sta1[N];
struct Block
{
int L,R,top,pres,preg,pretop,suftop;
node stk[N],pre[M],suf[M];
}f[M];
int block,n,q,tot,tail,top,l,r,r0,ans,sum,Y,Y1,Y2,ret,top1,gg,ss;
int be[N],a[N],lim[N],G[M][M],S[M][M];
int cmp(node x,node y)
{
return x.g < y.g;
}
void Pre()
{
for (int num = 1 ; num <= tot ; num++)
{
int l_ = F.L,r_ = F.R,K = F.L - 1;
tail = 0;
memset(G,0x3f,sizeof(G));
for (int i = l_ ; i <= r_ ; i++)
{
for (int j = i ; j <= r_ ; j++)
{
G[i - K][j - K] = min(lim[j],G[i - K][j - K - 1] + a[j]),S[i - K][j - K] = S[i - K][j - K - 1] + a[j];
b[++tail].g = G[i - K][j - K];
b[tail].s = S[i - K][j - K];
}
}
for (int i = l_ ; i <= r_ ; i++) F.pre[i - K].g = G[1][i - K],F.pre[i - K].s = S[1][i - K];
for (int i = r_ ; i >= l_ ; i--) F.suf[i - K].g = G[i - K][r_ - K],F.suf[i - K].s = S[i - K][r_ - K];
F.pres = F.pre[r_ - K].s,F.preg = F.pre[r_ - K].g;
sort(F.pre + 1,F.pre + F.R - F.L + 2,cmp);
sort(F.suf + 1,F.suf + F.R - F.L + 2,cmp);
sort(b + 1,b + tail + 1,cmp);
for (int i = 1 ; i <= tail ; i++)
{
while (F.stk[F.top].s < b[i].s && F.top) F.top--;
F.stk[++F.top].s = b[i].s;
F.stk[F.top].g = b[i].g;
}
top = top1 = 0;
memset(sta,0,sizeof(sta));
memset(sta1,0,sizeof(sta1));
for (int i = 1 ; i <= F.R - F.L + 1 ; i++)
{
while (sta[top].s < F.pre[i].s && top) top--;
sta[++top].s = F.pre[i].s;
sta[top].g = F.pre[i].g;
while (sta1[top1].s < F.suf[i].s && top1) top1--;
sta1[++top1].s = F.suf[i].s;
sta1[top1].g = F.suf[i].g;
}
F.pretop = top,F.suftop = top1;
memcpy(F.pre,sta,sizeof(F.pre));
memcpy(F.suf,sta1,sizeof(F.suf));
}
}
int find(int num,int val)
{
int l = 1,r = F.top,Ans = 0;
while (l <= r)
{
int mid = l + r >> 1;
(F.stk[mid].g >= F.stk[mid].s + val) ? Ans = mid,r = mid - 1 : l = mid + 1;
}
return (!Ans) ? F.stk[r].g : max(F.stk[Ans - 1].g,F.stk[Ans].s + val);
}
int find1(int num,int val)
{
int l = 1,r = F.pretop,Ans = 0;
while (l <= r)
{
int mid = l + r >> 1;
(F.pre[mid].g >= F.pre[mid].s + val) ? Ans = mid,r = mid - 1 : l = mid + 1;
}
return (!Ans) ? F.pre[r].g : max(F.pre[Ans - 1].g,F.pre[Ans].s + val);
}
int find2(int num,int val)
{
int l = 1,r = F.suftop,Ans = 0;
while (l <= r)
{
int mid = l + r >> 1;
(F.suf[mid].g >= F.suf[mid].s + val) ? Ans = mid,r = mid - 1 : l = mid + 1;
}
return (!Ans) ? F.suf[r].g : max(F.suf[Ans - 1].g,F.suf[Ans].s + val);
}
int main()
{
scanf("%d%d",&n,&q);
block = sqrt(n);
for (int i = 1 ; i <= n ; i++) scanf("%d",&a[i]);
for (int i = 1 ; i <= n ; i++) scanf("%d",&lim[i]),be[i] = (i - 1) / block + 1;
tot = (n - 1) / block + 1;
for (int i = 1 ; i <= tot ; i++) f[i].L = (i - 1) * block + 1,f[i].R = min(i * block,n);
Pre();
while (q--)
{
scanf("%d%d%d",&l,&r,&r0);
Y = ans = sum = r0;
if (be[l] == be[r])
{
sum = r0;
for (int i = l ; i <= r ; i++) sum += a[i],sum = min(sum,max(lim[i],r0)),ans = max(ans,sum);
printf("%d\n",ans);
continue;
}
for (int i = l ; i <= f[be[l]].R ; i++)
sum += a[i],sum = min(sum,max(lim[i],r0)),ans = max(ans,sum),Y += a[i],Y = min(Y,max(lim[i],r0));
for (int i = be[l] + 1 ; i <= be[r] - 1 ; i++)
{
ret = find(i,r0);
ans = max(ans,ret);
ret = find1(i,Y);
ans = max(ans,ret);
Y1 = min(Y + f[i].pres,f[i].preg);
Y2 = find2(i,r0);
Y = max(r0,max(Y1,Y2));
}
sum = max(r0,Y);
for (int i = f[be[r]].L ; i <= r ; i++) sum += a[i],sum = min(sum,max(lim[i],r0)),ans = max(ans,sum);
printf("%d\n",ans);
}
return 0;
}