CF1516D 不互素区间划分 - 倍增/分块维护跳链
根号暴力吊打
划分在一个区间内的数要求两两互质
比较容易求出每一个数下一个与它不互素的数:
1) 预处理出素数和最小质因子, 可以在
2) 倒顺枚举 , 记录每个素数作为因子最早出现的位置。分解当前数时将每个素因子最早出现的位置取
考虑暴力怎么做。给定一个固定的询问区间 , 从左到右贪心选择最靠右的端点满足题意。记从
则有:
这个可以
如果要预处理出所有
借助
这个借助
当然考场犯浑选手想的是分块维护跳的过程, 还写挂了。
需要维护两个值,
然后就是经典的跳法了, 不在同一块就整块跳过, 在同一块就一步步跳, 理论上一次查询复杂度是
然而跑得飞快, 基本上吊打倍增写法。
#include <iostream>
#include <cmath>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
const int maxn = 1e5+10;
int Prime[maxn], vp[maxn], minf[maxn], tot;
void seive(int n) {
minf[1] = 1;
rep(i,2,n) {
if(!vp[i]) vp[i] = 1, Prime[++tot] = i, minf[i] = i;
for(int j = 1; 1ll * i * Prime[j] <= n; ++j) {
vp[i*Prime[j]] = 1, minf[i*Prime[j]] = Prime[j];
if(i % Prime[j] == 0) break;
}
}
}
#define gb(x) ((x-1)/blk+1)
#define hob(x) ((x-1)*blk+1)
#define eob(x) (min(n,(x)*blk))
int a[maxn], n;
int lp[maxn], nex[maxn];
int nstp[maxn], nxp[maxn], blk;
void prework() {
blk = min(n, (int)sqrt(n)+1);
rep(i,1,1e5) lp[i] = n + 1; rep(i,1,n+1) nex[i] = n + 1;
seive(1e5);
per(i,n,1) {
int m = a[i]; nex[i] = nex[i+1];
while(m != 1) {
int t = minf[m];
while(m % t == 0) m /= t;
nex[i] = min(nex[i], lp[t]); lp[t] = i;
}
}
per(i,n,1) {
nex[i] <= eob(gb(i)) ?
(nstp[i] = nstp[nex[i]] + 1, nxp[i] = nxp[nex[i]]) :
(nstp[i] = 1, nxp[i] = nex[i]);
}
}
int main() {
if(fopen("yl.in", "r")) {
freopen("yl.in", "r", stdin);
freopen("yl.out", "w", stdout);
}
int q; scanf("%d %d", &n, &q);
rep(i,1,n) scanf("%d", a + i);
prework();
int l, r;
rep(qr,1,q) {
int ans = 0;
scanf("%d %d", &l, &r);
while(gb(l) < gb(r)) ans += nstp[l], l = nxp[l];
while(l <= r) ++ans, l = nex[l];
printf("%d\n", ans);
}
return 0;
}