[Luogu] CF515E Drazil and Park
阿新 • • 發佈:2020-11-16
Description
有一隻猴子,他生活在一個環形的公園裡。有\(n\)棵樹圍繞著公園。第\(i\)棵樹和第\(i+1\)棵樹之間的距離是\(d_i\),而第\(n\)棵樹和第一棵樹之間的距離是\(d_n\)。第\(i\)棵樹的高度是\(h_i\) 。
這隻猴子每天要進行晨跑。晨跑的步驟如下:
- 他先選擇兩棵樹;
- 然後爬上第一棵樹;
- 再從第一棵樹上下來,接著圍繞著公園跑(有兩個可能的方向)到第二棵樹,然後爬上第二棵樹;
- 最後從第二棵樹上下來。
但是有一些小孩會在連續的一些樹上玩耍。所以猴子不能經過這些樹。
比如現在猴子選擇的第\(x\)棵和第\(y\)棵樹,那麼該早晨他消耗的能量是\(2(h_x+h_y)+dist(x,y)\)
現在給出第\(i\)天,孩子們會在第\(a_i\)棵樹和\(b_i\)棵樹之間玩耍。具體的,如果\(a_i≤b_i\) ,那麼孩子玩耍的區間就是 \([a_i,b_i]\),否則孩子玩耍的區間就是\([ai,n]⋃[1,bi]\) 。
請幫助這隻猴子找出兩棵樹,讓他晨跑的時候他能夠消耗最大的能量。
Solution
可能會想到分別最大化,但這時求出來的\((x,y)\)很有可能不是同一對。
所以先斷環成鏈,再對\(d_i\)做一遍字首和。這時題目就轉化為最大化\(2h_x+2h_y+sum_y-sum_x\)
要注意首先要依據題意,對詢問區間求補集,然後算出來的\(x\),\(y\)可能是一樣的,不符合題意,這時只需在\([l,pos-1]\)和\([pos+1,r]\)中分別查詢最值,在計算比較即可。所以\(ST\)表維護的是最值的下標。
Code
#include <bits/stdc++.h> using namespace std; #define ll long long int n, m, lg[200005], d[200005], h[200005]; ll s1[200005], s2[200005], sum[200005], mn[200005][20], mx[200005][20]; int read() { int x = 0, fl = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();} while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();} return x * fl; } int query1(int x, int y) { if (x > y) return 0; int k = lg[y - x + 1]; ll p = s1[mx[x][k]], q = s1[mx[y - (1 << k) + 1][k]]; if (p > q) return mx[x][k]; else return mx[y - (1 << k) + 1][k]; } int query2(int x, int y) { if (x > y) return 0; int k = lg[y - x + 1]; ll p = s2[mn[x][k]], q = s2[mn[y - (1 << k) + 1][k]]; if (p < q) return mn[x][k]; else return mn[y - (1 << k) + 1][k]; } int main() { n = read(); m = read(); for (int i = 1; i <= n; i ++ ) { d[i % n + 1] = read(); d[i % n + n + 1] = d[i % n + 1]; } for (int i = 1; i <= n; i ++ ) { h[i] = read(); h[i] <<= 1; h[i + n] = h[i]; } for (int i = 1; i <= (n << 1); i ++ ) sum[i] = sum[i - 1] + (ll)(d[i]), lg[i] = (int)(log2((double)(i))); s1[0] = -2e15; s2[0] = 2e15; for (int i = 1; i <= (n << 1); i ++ ) { s1[i] = sum[i] + h[i]; s2[i] = sum[i] - h[i]; mn[i][0] = mx[i][0] = i; } for (int j = 1; j <= 19; j ++ ) { for (int i = 1; i + (1 << j) <= (n << 1); i ++ ) { ll x, y; x = s1[mx[i][j - 1]], y = s1[mx[i + (1 << (j - 1))][j - 1]]; if (x > y) mx[i][j] = mx[i][j - 1]; else mx[i][j] = mx[i + (1 << (j - 1))][j - 1]; x = s2[mn[i][j - 1]], y = s2[mn[i + (1 << (j - 1))][j - 1]]; if (x < y) mn[i][j] = mn[i][j - 1]; else mn[i][j] = mn[i + (1 << (j - 1))][j - 1]; } } while (m -- ) { int l = read(), r = read(), pos1, pos2; if (l <= r) pos1 = query1(r + 1, l + n - 1), pos2 = query2(r + 1, l + n - 1); else pos1 = query1(r + 1, l - 1), pos2 = query2(r + 1, l - 1); if (pos1 != pos2) printf("%lld\n", s1[pos1] - s2[pos2]); else { int pos3, pos4; ll res1, res2; if (l <= r) { pos3 = query1(r + 1, pos1 - 1), pos4 = query1(pos1 + 1, l + n - 1), res1 = max(s1[pos3], s1[pos4]) - s2[pos2]; pos3 = query2(r + 1, pos2 - 1), pos4 = query2(pos2 + 1, l + n - 1), res2 = s1[pos1] - min(s2[pos3], s2[pos4]); } else { pos3 = query1(r + 1, pos1 - 1), pos4 = query1(pos1 + 1, l - 1), res1 = max(s1[pos3], s1[pos4]) - s2[pos2]; pos3 = query2(r + 1, pos2 - 1), pos4 = query2(pos2 + 1, l - 1), res2 = s1[pos1] - min(s2[pos3], s2[pos4]); } printf("%lld\n", max(res1, res2)); } } return 0; }