题目
http://acm.hdu.edu.cn/showproblem.php?pid=4777
分析
离线+树状数组
去年现场赛时写了树套树,今天想了想树状数组的做法
首先处理出每个点前面和后面最近的不互质的数的位置,分别记为prev[]和next[]
然后把每个点挂在next[]下面
再把询问离线,挂在右端点上
从1到n枚举i,在i位置+1,在prev[i]位置-1
再把以i为next的点-1,对应的prev位置+1
然后把以这个点为有右端点的询问通过区间和查询即可
代码
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <vector> using namespace std; template <class T> inline void gmax(T &a, T b) { if (a < b) { a = b; } } template <class T> inline void gmin(T &a, T b) { if (a > b) { a = b; } } const int MAX_N = 200005; int cntPrime; int prime[MAX_N]; int minPrime[MAX_N]; bool isPrime[MAX_N]; int n, m; int weight[MAX_N]; int c[MAX_N]; int prev[MAX_N]; int next[MAX_N]; int last[MAX_N]; int ans[MAX_N]; vector<int> events[MAX_N]; vector< pair<int, int> > queries[MAX_N]; void makePrime() { for (int i = 1; i <= 200000; i++) { minPrime[i] = i; isPrime[i] = true; } isPrime[1] = false; for (int i = 2; i <= 200000; i++) { if (isPrime[i]) { prime[cntPrime++] = i; } for (int j = 0; j < cntPrime && prime[j] * i <= 200000; j++) { minPrime[prime[j] * i] = prime[j]; isPrime[prime[j] * i] = false; if (prime[j] == minPrime[i]) { break; } } } } inline int lowbit(int i) { return i & (-i); } void add(int x, int val) { for (int i = x; i <= n; i += lowbit(i)) { c[i] += val; } } int get(int x) { int ret = 0; for (int i = x; i; i -= lowbit(i)) { ret += c[i]; } return ret; } int main() { // freopen("in.txt", "r", stdin); makePrime(); while (scanf("%d %d", &n, &m), n || m) { for (int i = 1; i <= n; i++) { events[i].clear(); queries[i].clear(); } for (int i = 1; i <= n; i++) { scanf("%d", &weight[i]); } { for (int i = 0; i < cntPrime; i++) { last[prime[i]] = 0; } for (int i = 1; i <= n; i++) { int x = weight[i]; prev[i] = 0; while (x != 1) { gmax(prev[i], last[minPrime[x]]); x /= minPrime[x]; } x = weight[i]; while (x != 1) { last[minPrime[x]] = i; x /= minPrime[x]; } } } { for (int i = 0; i < cntPrime; i++) { last[prime[i]] = n + 1; } for (int i = n; i >= 1; i--) { int x = weight[i]; next[i] = n + 1; while (x != 1) { gmin(next[i], last[minPrime[x]]); x /= minPrime[x]; } x = weight[i]; while (x != 1) { last[minPrime[x]] = i; x /= minPrime[x]; } } } for (int i = 1; i <= n; i++) { if (next[i] <= n) { events[next[i]].push_back(i); } } for (int i = 1; i <= m; i++) { int l, r; scanf("%d %d", &l, &r); queries[r].push_back(make_pair(l, i)); } memset(c, 0, sizeof(c)); for (int i = 1; i <= n; i++) { add(i, 1); if (prev[i]) { add(prev[i], -1); } int cntEvent = (int)events[i].size(); for (int j = 0; j < cntEvent; j++) { int x = events[i][j]; add(x, -1); if (prev[x]) { add(prev[x], 1); } } int cntQuery = (int)queries[i].size(); for (int j = 0; j < cntQuery; j++) { ans[queries[i][j].second] = get(i) - get(queries[i][j].first - 1); } } for (int i = 1; i <= m; i++) { printf("%d\n", ans[i]); } } return 0; }