日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

@bzoj - 2388@ 旅行规划

發布時間:2023/12/2 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 @bzoj - 2388@ 旅行规划 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • @description@
  • @solution@
  • @accepted code@
  • @details@

@description@

請你維護一個序列,支持兩種操作:
(1)某個區間 [x, y] 內的數同時加上一個增量 k。
(2)詢問某一個區間 [x, y] 中從 1 開始的最大前綴和。

input
第一行給出一個整數 n。n <= 100000。接下來一行 n 個整數表示序列的初始值。
第三行給出一個整數 m,m <= 100000。接下來 m 行每行一個操作。
(1) 0 x y k:表示給區間 [x, y] 同時加上 k。
(2) 1 x y:詢問區間 [x, y]。
output
對于每個詢問,輸出一個整數表示最大前綴和。

sample input
5
1 8 -8 3 -7
3
1 1 5
0 1 3 6
1 2 4
sample output
9
22

@solution@

我們考慮直接維護前綴和序列,則操作(2)就是在查詢區間最大值。

而對于操作(1),我們相當于兩部分操作:
對于 x <= i <= y,給 i 位置加上 (i-x+1)*k;對于 y < i,給 i 位置加上 (y-x+1)*k。
前一個可以拆成 (-x+1)*k + i*k,是常數 + 系數*位置的形式;后面那個也可以看成這種形式,只是位置前面的系數為 0。

看起來還是不好維護,但是我們可以注意到這樣一件事情:對于某一個位置 i,它的值總是形如 k*i + b 的形式。
直線解析式。所以我們考慮用幾何方法來維護這種東西。幾何方法當然首先想到凸包。

每次操作相當于給區間內所有點的斜率與截距同時加上增量,手算一下會發現這個區間相鄰兩點之間的斜率也會同時增加 k,這樣也就是說這個區間的凸包形狀不會變化。

線段樹不太好搞(況且這道題時限 50s ),我們考慮使用分塊算法來維護凸包。
修改時,散塊暴力修改并重構凸包,整塊打標記,記錄這個區間整體的斜率與截距變化量。
查詢時,散塊暴力求答案,整塊凸包上二分。

時間復雜度 \(O(n\sqrt{n}\log n)\)

@accepted code@

#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int MAXN = 100000; const int BLOCK = 320; const ll INF = (1ll<<62); ll sp[BLOCK + 5], b1[MAXN + 5], b2[BLOCK + 5]; int le[BLOCK + 5], ri[BLOCK + 5], num[MAXN + 5]; int stk[MAXN + 5], tp[BLOCK + 5], n, m, bcnt = 0; ll get_ans(int x) {return sp[num[x]]*x + b1[x] + b2[num[x]]; } ll query(int x) {int l = le[x], r = tp[x];while( l < r ) {int mid = (l + r) >> 1;if( get_ans(stk[mid]) >= get_ans(stk[mid+1]) ) r = mid;else l = mid + 1;}return get_ans(stk[r]); } void push_tag(int x) {for(int i=le[x];i<=ri[x];i++)b1[i] = get_ans(i);sp[x] = b2[x] = 0; } void build(int x) {tp[x] = le[x] - 1;for(int i=le[x];i<=ri[x];i++) {while( tp[x] > le[x] && (get_ans(i) - get_ans(stk[tp[x]]))*(stk[tp[x]] - stk[tp[x] - 1]) >= (get_ans(stk[tp[x]])-get_ans(stk[tp[x] - 1]))*(i - stk[tp[x]]) )tp[x]--;stk[++tp[x]] = i;} } void init() {for(int i=0;i<n;i++) {if( i % BLOCK == 0 ) {num[i] = (++bcnt);le[num[i]] = ri[num[i]] = i;sp[num[i]] = b2[num[i]] = 0;}else ri[num[i] = bcnt]++;} } int main() {scanf("%d", &n); init();for(int i=0;i<n;i++)scanf("%lld", &b1[i]), b1[i] += b1[i-1];for(int i=1;i<=bcnt;i++)build(i);scanf("%d", &m);for(int i=1;i<=m;i++) {int op, x, y; ll k;scanf("%d%d%d", &op, &x, &y);x--, y--;if( op == 0 ) {scanf("%lld", &k);if( num[x] != num[y] ) {push_tag(num[x]), push_tag(num[y]);for(int i=x;i<=ri[num[x]];i++)b1[i] += k*(i-x+1);for(int i=le[num[y]];i<=y;i++)b1[i] += k*(i-x+1);for(int i=y+1;i<=ri[num[y]];i++)b1[i] += k*(y-x+1);build(num[x]), build(num[y]);for(int i=num[x]+1;i<=num[y]-1;i++)sp[i] += k, b2[i] += k*(-x+1);}else {push_tag(num[x]);for(int i=x;i<=y;i++)b1[i] += k*(i-x+1);for(int i=y+1;i<=ri[num[y]];i++)b1[i] += k*(y-x+1);build(num[x]);}for(int i=num[y]+1;i<=bcnt;i++)b2[i] += k*(y-x+1);}else {ll ans = -INF;if( num[x] != num[y] ) {for(int i=x;i<=ri[num[x]];i++)ans = max(ans, get_ans(i));for(int i=le[num[y]];i<=y;i++)ans = max(ans, get_ans(i));for(int i=num[x]+1;i<=num[y]-1;i++)ans = max(ans, query(i));}else {for(int i=x;i<=y;i++)ans = max(ans, get_ans(i));}printf("%lld\n", ans);}} }

@details@

突然發現這是我第一次寫分塊?原來我以前從來沒寫過這種東西?
把分塊的左端點右端點以及每個點屬于哪個塊先預處理出來感覺比較好寫。
并且把塊的大小設置為常數也是一個不錯的懶人做法(雖然想想都知道這樣肯定常數大)。

轉載于:https://www.cnblogs.com/Tiw-Air-OAO/p/10262494.html

總結

以上是生活随笔為你收集整理的@bzoj - 2388@ 旅行规划的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。