JZOJ-senior-5936. 【NOIP2018模擬10.29】逛公園
阿新 • • 發佈:2018-11-08
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)
{