1. 程式人生 > >BZOJ2388:旅行規劃(travel)——分塊凸包

BZOJ2388:旅行規劃(travel)——分塊凸包

經濟 push 觀光 def src 找到 變化 find 行為

題目

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)——分塊凸包