Fated_Shadow
2024-11-20 16:15:54
怎么全是神秘二分?双指针不好吗? 传送门
容易想到维护区间
直接暴力查询每个区间
看见了有人使用二分,但是考虑直接双指针。
单独考虑
那么,固定右端点时,使得
那么直接双指针即可。注意判断合法情况,统计答案时也注意区间长度奇偶问题。
时间复杂度
Talk is cheap, show me the code.
// Problem: P10403 「XSOI-R1」跳跃游戏
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P10403
// Memory Limit: 128 MB
// Time Limit: 750000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace FastIO {
char gc() {
static char buf[1 << 24], *is = buf, *it = buf;
if (is == it) is = buf, it = is + fread(buf, 1, 1 << 24, stdin);
return is == it ? EOF : *is ++;
}
char out[1 << 24];
int len;
void flush() {fwrite(out, 1, len, stdout), len = 0;}
void pc(char x) {if (len == 1 << 24) flush(); out[len ++] = x;}
struct Flusher {~Flusher() {flush();} } Fls;
// #define gc() getchar()
// #define pc(X) putchar(X)
inline void read(char& ch) {ch = gc();}
inline void read(char *s) {
char ch = gc();
while (ch < 33 || ch > 126) ch = gc();
for (; ch >= 33 && ch <= 126; ch = gc()) *s++ = ch;
*s = '\0';
}
template <class T>
inline void read(T &x) {
char c, flag = 0;
while ((c = gc()) < '0' || c > '9') flag |= c == '-';
x = c & 15;
while ((c = gc()) >= '0' && c <= '9')
x = (x << 3) + (x << 1) + (c & 15);
if (flag) x = ~x + 1;
}
template <class T, class ...T1>
inline void read(T &x, T1 &...x1) {read(x), read(x1...);}
template <class T>
inline void _put(T x) {if(x > 9) _put(x / 10); pc((x % 10) | 48);}
template <class T>
inline void write(T x) {if(x < 0) pc('-'), x = ~x + 1; _put(x);}
template <> inline void write(char x) {pc(x);}
template <class T, class ...T1>
inline void write(T x, T1 ...x1) {write(x), write(x1...);}
}
using FastIO::read;
using FastIO::write;
template <class T>
inline bool chkmin(T &x, T y) {return x > y ? x = y, 1 : 0;}
template <class T>
inline bool chkmax(T &x, T y) {return x < y ? x = y, 1 : 0;}
const int N = 6e5 + 10, K = 20;
int n, a[N], st[N][K], lg[N], ans;
int gcd(int a, int b) {return b ? gcd(b, a % b) : a;}
int query(int l, int r) {
int k = lg[r - l + 1]; return gcd(st[l][k], st[r - (1 << k) + 1][k]);
}
int f2(int x) {x /= 2; return (x + 1) * x;}
int f3(int x) {return (x + (x % 2 ? 1 : 0)) * ((x + 1) / 2) / 2;}
signed main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
read(n), lg[0] = -1;
for(int i = 1; i <= n; ++i)
read(a[i]), st[i][0] = a[i], lg[i] = lg[i >> 1] + 1;
for(int k = 1; k <= lg[n]; ++k) for(int i = 1; i + (1 << k) - 1 <= n; ++i)
st[i][k] = gcd(st[i][k - 1], st[i + (1 << (k - 1))][k - 1]);
for(int i = 1, l = 1, r = 1; i <= n; ++i) {
if(a[i] & 1) l = i + 1, r = i;
else {
while(r < i && query(r + 1, i) <= 2) ++r;
while(l <= r && query(l, i) < 2) ++l;
if(r >= l && query(r, i) == 2 && query(l, i) == 2)
ans += f2(i - l + 1) - f2(i - r);
}
}
for(int i = 1, l = 1, r = 1; i <= n; ++i) {
if(a[i] % 3) l = i + 1, r = i;
else {
while(r < i && query(r + 1, i) <= 3) ++r;
while(l <= r && query(l, i) < 3) ++l;
if(r >= l && query(r, i) == 3 && query(l, i) == 3)
ans += f3(i - l + 1) - f3(i - r);
}
}
write(ans);
return 0;
}
实际上本题时间瓶颈在于 st 表,使用二分与使用双指针并无太明显的差距,不过能写更优就是了(实际上还是慢的一批)。如有问题,欢迎指正。