bzoj2119: 股市的預測 Hash+二分 調和級數
阿新 • • 發佈:2019-01-03
bzoj2119: 股市的預測
分析
差分之後就是求有多少段相距
的相同子串。
一種思路是,考慮長度為
的有多少種。
一個神仙做法是,每隔
取一個關鍵點,那麼每個長度為
的子串最多隻會覆蓋一個關鍵點,考慮過某個關鍵點的答案,這樣一定能做到不重不漏。
也就是求把一個定長度的區間兩端過這個關鍵點
左右滑動的最大範圍。
令
求
的lcs和
的lcp即可。
用字尾陣列/自動機可以做到預處理
查詢
但博主比較懶,直接上了
+二分。
複雜度
程式碼
#include<bits/stdc++.h>
typedef unsigned long long ULL;
const ULL se = 998244353; const int N = 5e4 + 10;
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
ULL H[N], pw[N]; int n, B, s[N], Ans;
ULL Hash(int l, int r) {return H[r] - pw[r - l + 1] * H[l - 1];}
int lcp(int a, int b) {
int l = 0, r = n - b, ans = -1;
for(;l <= r;) {
int m = l + r >> 1;
Hash(a, a + m) == Hash(b, b + m) ? ans = m, l = m + 1 : r = m - 1;
}
return ans + 1;
}
int lcs(int a, int b) {
int l = 0, r = a - 1, ans = -1;
for(;l <= r;) {
int m = l + r >> 1;
Hash(a - m, a) == Hash(b - m, b) ? ans = m, l = m + 1 : r = m - 1;
}
return ans + 1;
}
void Solve(int L) {
for(int i = 1;i + B + L <= n; i += L)
if(s[i] == s[i + B + L]) {
int r = lcp(i, i + B + L), l = lcs(i, i + B + L);
l = std::min(l, L); r = std::min(r, L);
Ans += std::max(l + r - L, 0);
}
}
int main() {
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
n = ri(); B = ri();
for(int i = 1;i <= n; ++i) {
s[i] = ri();
s[i - 1] = s[i] - s[i - 1];
}
--n;
pw[0] = 1; for(int i = 1;i <= n; ++i) pw[i] = pw[i - 1] * se;
for(int i = 1;i <= n; ++i) H[i] = H[i - 1] * se + s[i] % se;
for(int L = 1;(L << 1) + B <= n; ++L) Solve(L);
printf("%d\n", Ans);
return 0;
}