题目大意:长度n的序列, m个询问区间[L, R], 问区间内的所有子段的不同GCD值有多少种. 子段就是表示是要连续的a[]
思路:固定右端点,预处理出所有的gcd,每次都和i-1的gcd比较,然后不断放入gcd即可。
然后就是树状数组的更新,枚举右端点即可。然后我们知道,大区间不如小区间来的实惠,所以我们每次有重复gcd出现的时候,都要把大区间更换成小区间即可
下午脑子有点不清楚。。。2333看别人的博客蒙了好久
//看看会不会爆int!数组会不会少了一维! //取物问题一定要小心先手胜利的条件 #include <bits/stdc++.h> using namespace std; #define LL long long #define ALL(a) a.begin(), a.end() #define pb push_back #define mk make_pair #define fi first #define se second /* 先总体说一下思路:求出区间的gcd,再对右进行排序,再枚举右端即可 */ const int maxn = 1e5 + 5; int tree[maxn], a[maxn], pre[maxn * 10], ans[maxn]; int n, q; vector<pair<int, int> >qq[maxn], v[maxn]; int lowbit(int x) {return x & -x;} int gcd(int a, int b){ return b == 0 ? a : gcd(b, a % b); } void update(int x, int val){ for (int i = x; i <= n; i += lowbit(i)){ tree[i] += val; } } int sum(int x){ int ans = 0; for (int i = x; i > 0; i -= lowbit(i)) ans += tree[i]; return ans; } void solve(){ memset(pre, 0, sizeof(pre)); memset(tree, 0, sizeof(tree)); for (int i = 1; i <= n; i++){ int len = v[i].size(); for (int j = 0; j < len; j++){ pair<int, int> g = v[i][j]; if (pre[g.first]) update(pre[g.first], -1); pre[g.first] = g.second; update(g.second, 1); } len = qq[i].size(); for (int j = 0; j < len; j++){ pair<int, int> p = qq[i][j]; ans[p.second] = sum(i) - sum(p.first - 1); } } for (int i = 1; i <= q; i++){ printf("%d ", ans[i]); } } int main(){ while (scanf("%d%d", &n, &q) == 2){ for (int i = 1; i <= n; i++){ scanf("%d", a + i); v[i].clear(); qq[i].clear(); } for (int i = 1; i <= n; i++){ int len = v[i - 1].size(); int val = a[i], pos = i; for (int j = 0; j < len; j++){ pair<int, int> p = v[i - 1][j]; int g = gcd(p.first, val); if (g != val) { v[i].pb(mk(val, pos));///要优先知道那一个区间里面的gcd val = g; pos = p.second; } } v[i].pb(mk(val, pos)); } /* for (int i = 1; i <= n; i++){ int len = v[i].size(); for (int j = 0; j < len; j++){ printf("%d&%d ", v[i][j].first, v[i][j].second); } printf(" "); } */ for (int i = 1; i <= q; i++){ int l, r; scanf("%d%d", &l, &r); qq[r].pb(mk(l, i)); } solve(); } return 0; }