BZOJ2388:旅行規劃(travel)——分塊凸包
題目
OIVillage 是一個風景秀美的鄉村,為了更好的利用當地的旅遊資源,吸引遊客,推動經濟發展,xkszltl 決定修建了一條鐵路將當地 $n$ 個最著名的經典連接起來,讓遊客可以通過火車從鐵路起點( $1$ 號景點)出發,依次遊覽每個景區。為了更好的評價這條鐵路,xkszltl 為每一個景區都賦予了一個美觀度,而一條旅行路徑的價值就是它所經過的景區的美觀度之和。不過,隨著天氣與季節的變化,某些景點的美觀度也會發生變化。
xkszltl 希望為每位旅客提供最佳的旅行指導,但是由於遊客的時間有限,不一定能遊覽全部景區,然而他們也不希望旅途過於短暫,所以每個遊客都希望能在某一個區間內的車站結束旅程,而 xkszltl 的任務就是為他們選擇一個終點使得旅行線路的價值最大。可是當地的景點與前來觀光的旅客實在是太多了,xkszltl 無法及時完成任務,於是找到了準備虐殺 NOI2019 的你,希望你能幫助他完成這個艱巨的任務。
【輸入格式】
第一行給出一個整數 $n$,接下來一行給出 $n$ 的景區的初始美觀度。
第三行給出一個整數 $m$,接下來 $m$ 行每行為一條指令:
$1.~~~0~x~y~k$:表示將 $x$ 到 $y$ 這段鐵路邊上的景區的美觀度加上 $k$;
$2.~~~1~x~y$:表示有一名旅客想要在 $x$ 到 $y$ 這段(含 $x$ 與 $y$ )中的某一站下車,你需要告訴他最大的旅行價值。
【輸出格式】
對於每個詢問,輸出一個整數表示最大的旅行價值。
【樣例輸入】
5
1 8 -8 3 -7
3
1 1 5
0 1 3 6
1 2 4
【樣例輸出】
9
22
【數據範圍與提示】
對於 $20\%$ 的數據,$n,m≤3000$;
對於 $40\%$ 的數據,$n,m≤30000$;
對於 $50\%$ 的數據,$n,m≤50000$;
另外 $20\%$ 的數據,$n,m≤100000$,修改操作 $≤20$;
對於 $100\%$ 的數據,$n,m≤100000$。
題解
無力吐槽這組孱弱的樣例(過了樣例還是調出了 INF 個錯)
題目求得是區間最大前綴和,帶區間加值操作
然後發現線段樹無法在 $ O(\log n) $ 的時間內維護區間加上一次函數後的最大值
考慮分塊維護最大值
記 $ sum(i) $ 表示前綴和,$ num(i) $ 表示塊 $ i $ 加上的一次函數,$ d(i) $ 表示這個塊需要加上的常數,
那麽 $ sum(i)=num(i)\times i +d(i) $
發現最大值形如 $ y=kx+b $,然後就可以在塊上維護一個凸包
當塊上加一個一次函數時,凸包的點是不會改變的,那麽每次加值時就只要每次重構左右兩邊的兩個凸包即可
查詢時只要在每一個塊的凸包上二分即可
時間:修改時$ O(\sqrt{n}) $,查詢時 $ O(\sqrt{n}) $,所以總時間 $ O(m\sqrt{n}\log n) $
代碼
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define db double 4 #define _(d) while(d(isdigit(ch=getchar()))) 5 using namespace std; 6 int R(){ 7 int x;bool f=1;char ch;_(!)if(ch==‘-‘)f=0;x=ch^48; 8 _()x=(x<<3)+(x<<1)+(ch^48);return f?x:-x;} 9 const int N=1e5+5,M=1002; 10 int n,m,a[N],Sz,Mx,p[N],sz[M],tb[M][M]; 11 LL w[N],num[N],f[N],d[N]; 12 db getK(int i,int j){return (db)(w[i]-w[j])/(db)(i-j);} 13 void update(int x){ 14 int l=(x-1)*Sz+1,r=min(x*Sz,n),tot=0; 15 p[++tot]=l; 16 for(int i=l+1;i<=r;i++){ 17 while(tot>1&&getK(p[tot],p[tot-1])<getK(p[tot-1],i)) 18 tot--; 19 p[++tot]=i; 20 } 21 sz[x]=tot; 22 for(int i=1;i<=tot;i++)tb[x][i]=p[i]; 23 return; 24 } 25 void pushdown(int x){ 26 LL o=num[x]; 27 for(int i=(x-1)*Sz+1;i<=min(x*Sz,n);i++) 28 o+=d[x],w[i]+=o; 29 num[x]=d[x]=0; 30 return; 31 } 32 void change(int l,int r,int k){ 33 int x=a[l],y=a[r];LL o=0; 34 pushdown(x); 35 for(int i=l;i<=min(x*Sz,r);i++) 36 o+=k,w[i]+=o; 37 update(x); 38 for(int i=x+1;i<y;i++) 39 num[i]+=o,d[i]+=k,o+=1ll*Sz*k; 40 if(x!=y){ 41 pushdown(y); 42 for(int i=(y-1)*Sz+1;i<=r;i++) 43 o+=k,w[i]+=o; 44 } 45 o=(r-l+1)*k; 46 for(int i=r+1;i<=min(n,y*Sz);i++)w[i]+=o; 47 update(y); 48 for(int i=y+1;i<=Mx;i++)num[i]+=o; 49 return; 50 } 51 LL find(int i){ 52 if(!i||i>n)return (LL)-2e18; 53 return (w[i]+num[a[i]]+d[a[i]]*(i-(a[i]-1)*Sz)); 54 } 55 LL work(int x){ 56 int l=1,r=sz[x]; 57 while(l<=r){ 58 int mid=(l+r)>>1; 59 LL t1=find(tb[x][mid-1]); 60 LL t2=find(tb[x][mid]); 61 LL t3=find(tb[x][mid+1]); 62 if (t1<t2 && t2<t3) l=mid+1; 63 else if (t1>t2 && t2>t3) r=mid-1; 64 else return t2; 65 } 66 return l; 67 } 68 LL query(int l,int r){ 69 int x=a[l],y=a[r];LL ans=-2e18; 70 for(int i=x+1;i<y;i++) 71 ans=max(ans,work(i)); 72 for(int i=l;i<=min(x*Sz,r);i++) 73 ans=max(ans,find(i)); 74 if(x!=y) 75 for(int i=(y-1)*Sz+1;i<=r;i++) 76 ans=max(ans,find(i)); 77 return ans; 78 } 79 int main(){ 80 n=R(),Sz=sqrt(n); 81 for(int i=1;i<=n;i++) 82 w[i]=w[i-1]+R(),a[i]=(i-1)/Sz+1; 83 Mx=a[n],m=R(); 84 for(int i=1;i<=Mx;i++)update(i); 85 for(int i=1;i<=m;i++){ 86 int op=R(),x=R(),y=R(),k; 87 if(op==1)printf("%lld\n",query(x,y)); 88 if(!op)k=R(),change(x,y,k); 89 } 90 return 0; 91 }View Code
BZOJ2388:旅行規劃(travel)——分塊凸包