JZOJ5936. 【NOIP2018模擬10.29】逛公園
題意:
資料範圍:
Analysis:
吼題啊。
這種題會有性質的,我們要根據性質去計算答案。
我們設
表示以
為初始值走完
~
最後的結果,貪心的想,在某個位置儘量大,結果越大,所以有:若
,則有
。
考慮一個區間以
為初始值的答案怎麼計算。
發現對於一個
,它在第一次被上限卡滿之後就是一般情況了,我們基於這個來分析一波性質。
若
在某個位置被卡滿,答案顯然是
。為方便表示設:
。
若沒被卡滿,答案是
表示
~
所有
值之和。
發現一個區間的答案就是兩者取
。
那麼考慮什麼情況下一個區間不優:
對於兩個區間
。若
的
都大於
的
。那麼
就沒用了。
若將有用的區間按
從大到小排序,發現
遞減,
遞增,若以初值
計算答案,不難發現最優值會是
最接近的位置,又由於之前的單調性,故可以二分。
這樣就可以快速計算一個區間的答案了。
這麼做啟發我們分塊預處理。
我們處理出每一個塊所有有用的區間,總數是
的。
那麼一個塊內的答案就可以二分了,散塊的話,我們發現一個位置貪心的要取大,我們可以從
開始取,若一個位置的值小於
,那麼將它變為
即可。
考慮跨越塊的怎麼處理。肯定是一個前面字尾和當前字首的拼接。
考慮字尾的答案為
,那麼也就是對當前塊要求最大的字首
,這也可以二分了。
因為第一條性質,我們只需要最大的
。那麼對於所有塊預處理出它的所有有用字首字尾,散塊暴力處理。
最大的
怎麼算,分類討論:
1.由之前的
跨過當前整塊。
2.當前塊的某個字尾(可以二分)。
3.
三者取
即可,至此本題解決,複雜度
。
Code:
# include<cstdio>
# include<cstring>
# include<algorithm>
# include<cmath>
using namespace std;
const int N = 4e4 + 5;
const int M = 2e2 + 5;
struct node
{
int g,s;
}q[M][N],pre[M][M],suf[M][M],c[M],sta[N];
int d[N],lim[N],s[N],t[M][3];
int L[M],R[M],pos[N],ps[M][N],su[M][N];
int n,Q;
inline int read()
{
int x = 0; char ch = getchar();
for (; ch < '0' || ch > '9' ; ch = getchar());
for (; ch >= '0' && ch <= '9' ; ch = getchar()) x = x * 10 + ch - '0';
return x;
}
bool cmp(node a,node b) { return a.g < b.g; }
inline int Br(int l,int r,int x)
{
int now = x,ans = x;
for (int i = l ; i <= r ; ++i)
{
now += d[i]; if (now > lim[i]) now = lim[i];
now = max(now,x),ans = max(ans,now);
} return ans;
}
inline int Cl(node *a,int Le)
{
int top = 0; sort(a + 1,a + Le + 1,cmp);
for (int i = 1 ; i <= Le ; ++i)
{
while (top && sta[top].s < a[i].s) --top;
sta[++top] = a[i];
}
for (int i = top ; i ; --i) a[top - i + 1] = sta[i];
return top;
}
inline int find(node *a,int Le,int x)
{
int l = 1,r = Le,ret = Le + 1; a[Le + 1].g = 0;
while (l <= r)
{
int mid = (l + r) >> 1;
if (a[mid].s + x >= a[mid].g) r = mid - 1,ret = mid;
else l = mid + 1;
} return max(a[ret].g,a[ret - 1].s + x);
}
int main()
{
freopen("park.in","r",stdin);
freopen("park.out","w",stdout);
n = read(),Q = read(); int len = sqrt(n),m = n / len + (n % len ? 1 : 0);
for (int i = 1 ; i <= n ; ++i) d[i] = read(),s[i] = s[i - 1] + d[i];
for (int i = 1 ; i <= n ; ++i) lim[i] = read(),pos[i] = (i - 1) / len + 1;
for (int i = 1 ; i <= m ; ++i) L[i] = R[i - 1] + 1,R[i] = min(n,len * i);
for (int i = 1 ; i <= m ; ++i)
{
for (int j = L[i] ; j <= R[i] ; ++j)
{
int now = lim[j];
for (int k = j ; k <= R[i] ; ++k)
{
now += d[k]; if (now > lim[k]) now = lim[k];
q[i][++t[i][0]] = (node){now,s[k] - s[j - 1]};
}
}
t[i][0] = Cl(q[i],t[i][0]);
for (int j = R[i] ; j >= L[i] ; --j)
{
int now = lim[j];
for (int k = j + 1 ; k <= R[i] ; ++k)
{ now += d[k]; if (now > lim[k]) now = lim[k]; }
suf[i][++t[i][1]] = (node){now,s[R[i]] - s[j - 1]},su[i][j] = now;
} t[i][1] = Cl(suf[i],t[i][1]); int now = lim[L[i]];
for (int j = L[i] ; j <= R[i] ; ++j)
{ now += d[j]; if (now > lim[j]) now = lim[j]; pre[i][++t[i][2]] = (node){now,s[j] - s[L[i] - 1]},ps[i][j] = now; }
t[i][2] = Cl(pre[i],t[i][2]);
}
while (Q--)
{
int l = read(),r = read(),x = read(),ans = x,y = 0;
if (pos[l] == pos[r]) ans = max(ans,Br(l,r,x));
else
{
ans = max(ans,max(Br(l,R[pos[l]],x),Br(L[pos[r]],r,x)));
for (int i = pos[l] + 1 ; i < pos[r] ; ++i) ans = max(ans,find(q[i],t[i][0],x));
int top = 0;
for (int i = l ; i <= R[pos[l]] ; ++i) c[++top] = (node){su[pos[l]][i],s[R[pos[l]]] - s[i - 1]};
top = Cl(c,top); y = max(x,find(c,top,x));
for (int i = pos[l] + 1 ; i < pos[r] ; ++i)
{
ans = max(ans,find(pre[i],t[i][2],y));
y = max(x,max(min(ps[i][R[i]],s[R[i]] - s[L[i] - 1] + y),find(suf[i],t[i][1],x)));
} top = 0;
for (int i = L[pos[r]] ; i <= r ; ++i) c[++top] = (node){ps[pos[r]][i],s[i] - s[L[pos[r]] - 1]};
top = Cl(c,top); ans = max(ans,find(c,top,y));
} printf("%d\n",ans);
}
return 0;
}