1. 程式人生 > >JZOJ5936. 【NOIP2018模擬10.29】逛公園

JZOJ5936. 【NOIP2018模擬10.29】逛公園

題意:

資料範圍:

Analysis:

吼題啊。
這種題會有性質的,我們要根據性質去計算答案。
我們設 f ( l , r , x )

f(l,r,x) 表示以 x x 為初始值走完 l l ~ r
r
最後的結果,貪心的想,在某個位置儘量大,結果越大,所以有:若 a < b a < b ,則有 f
( l , r , a ) < = f ( l , r , b ) f(l,r,a)<=f(l,r,b)

考慮一個區間以 x 0 x_0 為初始值的答案怎麼計算。
發現對於一個 x 0 x_0 ,它在第一次被上限卡滿之後就是一般情況了,我們基於這個來分析一波性質。
x 0 x_0 在某個位置被卡滿,答案顯然是 f ( l , r , L l ) f(l,r,L_l) 。為方便表示設: G ( l , r ) = f ( l , r , L l ) G(l,r)=f(l,r,L_l)
若沒被卡滿,答案是 S ( l , r ) + x 0 , S ( l , r ) S(l,r)+x_0,S(l,r) 表示 l l ~ r r 所有 D D 值之和。
發現一個區間的答案就是兩者取 m i n min
那麼考慮什麼情況下一個區間不優:
對於兩個區間 [ l , r ] , [ l 1 , r 1 ] [l,r],[l1,r1] 。若 [ l , r ] [l,r] G , S G,S 都大於 [ l 1 , r 1 ] [l1,r1] G , S G,S 。那麼 [ l 1 , r 1 ] [l1,r1] 就沒用了。
若將有用的區間按 G G 從大到小排序,發現 G G 遞減, S S 遞增,若以初值 x 0 x_0 計算答案,不難發現最優值會是 G , S + x 0 G,S+x_0 最接近的位置,又由於之前的單調性,故可以二分。
這樣就可以快速計算一個區間的答案了。
這麼做啟發我們分塊預處理。
我們處理出每一個塊所有有用的區間,總數是 O ( n n ) O(n\sqrt{n}) 的。
那麼一個塊內的答案就可以二分了,散塊的話,我們發現一個位置貪心的要取大,我們可以從 l l 開始取,若一個位置的值小於 x 0 x_0 ,那麼將它變為 x 0 x_0 即可。
考慮跨越塊的怎麼處理。肯定是一個前面字尾和當前字首的拼接。
考慮字尾的答案為 y y ,那麼也就是對當前塊要求最大的字首 f ( l , r , y ) f(l,r,y) ,這也可以二分了。
因為第一條性質,我們只需要最大的 y y 。那麼對於所有塊預處理出它的所有有用字首字尾,散塊暴力處理。
最大的 y y 怎麼算,分類討論:
1.由之前的 y y 跨過當前整塊。
2.當前塊的某個字尾(可以二分)。
3. x 0 x_0
三者取 m a x max 即可,至此本題解決,複雜度 O ( ( q + n ) n log n ) O((q+n)\sqrt{n}\log{n})

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;
}