1. 程式人生 > >51nod1624 取余最長路 前綴和 + set

51nod1624 取余最長路 前綴和 + set

最長 main 一個 lin str pac 數據 可能 else

技術分享圖片

由於只有3行,因此只會會換行2次,假設$x, y$分別為這兩次的換行點

那麽答案為$S[1][x] +S[2][y] - S[2][x - 1] + S[3][n] - S[3][y - 1]$

其中,$S[i]$表示第$i$行的前綴和

令$a[x] = S[1][x] - S[2][x - 1], b[y] = S[2][y] - S[3][y - 1]$

考慮枚舉$x$,那麽問題轉化為詢問在一堆數中求一個數$k$使得$v (= a[x] + S[3][n]) + k \;mod\;p$最大

分兩種情況考慮,第一種$v + k \in [v, mod - 1]$,那麽$k \in [0, mod - k - 1]$,並且$k$越大越好

第二種不如第一種好,但有可能不得不選,$v + k \in [1, v - 1]$,同樣時$k$越大越好

也就是說,需要一種支持插入,查詢前驅和最大值的數據結構,$set$就可以

註:倒敘枚舉$x$,可以做到不刪除

復雜度$O(n \log n)$

#include <set>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

extern inline char
gc() { static char RR[23456], *S = RR + 23333, *T = RR + 23333; if(S == T) fread(RR, 1, 23333, stdin), S = RR; return *S ++; } inline int read() { int p = 0, w = 1; char c = gc(); while(c > 9 || c < 0) { if(c == -) w = -1; c = gc(); } while(c >= 0 && c <=
9) p = p * 10 + c - 0, c = gc(); return p * w; } #define ll long long #define ri register int #define sid 200050 int n, ans, mod; int s[4][sid], a[sid], b[sid]; set <int> ex; int main() { n = read(); mod = read(); for(ri i = 1; i <= 3; i ++) for(ri j = 1; j <= n; j ++) s[i][j] = (s[i][j - 1] + read()) % mod; for(ri i = 1; i <= n; i ++) a[i] = (s[1][i] - s[2][i - 1] + mod) % mod; for(ri i = 1; i <= n; i ++) b[i] = (s[2][i] - s[3][i - 1] + mod) % mod; int der = s[3][n]; ex.insert(1); for(ri i = n; i >= 1; i --) { ex.insert(-b[i]); int v = (der + a[i]) % mod; int p = *ex.lower_bound(-(mod - 1 - v));; if(p == 1) ans = max(ans, (v - *(++ ex.begin())) % mod); else ans = max(ans, v + -p); } printf("%d\n", ans); return 0; }

51nod1624 取余最長路 前綴和 + set