这道题目用到的知识和 【HDU 1695 GCD】是一样的。
都可以抽象出一个子问题:判断一个数s同区间(x, y)里面有多少个数互素——用容斥原理可以解决。
这道题目不是统计互素的个数,而是统计与s互素的数的和,其实没有区别,求出个数之后等差数列求和就可以。
还有一个修改操作:由于修改的次数不是很多,可以单独考虑更改的数字——注意判重(代码中有注释)
#include <cstdio> #include <algorithm> #include <cstring> #include <iostream> #include <vector> using namespace std; #define N 400010 typedef long long LL; int n, m; struct node { int idx, v; node() {} node(int x, int y): idx(x), v(y) {} }; vector<node> c; bool is[N]; vector<int> pr; void prime() { memset(is, true, sizeof(is)); pr.clear(); pr.push_back(2); for (int i=3; i<N; i+=2) if (is[i]) { pr.push_back(i); for (int j=i+i; j<N; j+=i) is[j] = false; } } LL cal(int x, int p, vector<int> &g) { LL ret = (LL)x*(x+1)/2; for (int s=1; s<(1<<g.size()); s++) { int cnt = 0; int v = 1; for (int i=0; i<g.size(); i++) if (s & (1<<i)) { cnt++; v *= g[i]; } LL k = x / v; k = k*(k+1)*v/2; if (cnt % 2 == 1) ret -= k; else ret += k; } for (int i=0; i<c.size(); i++) if (c[i].idx <= x) { if (__gcd(c[i].idx, p) == 1)ret -= c[i].idx; if (__gcd(c[i].v, p) == 1) ret += c[i].v; } return ret; } void work(int x, int y, int p) { vector<int> g; int k = p; for (int i=0; i<pr.size() && pr[i]<=k; i++) if (k % pr[i] == 0) { g.push_back(pr[i]); while (k % pr[i] == 0) k /= pr[i]; } printf("%I64d\n", cal(y, p, g)-cal(x-1, p, g)); } int main() { prime(); int T, x, y, o, p; scanf("%d", &T); while (T--) { scanf("%d%d", &n, &m); c.clear(); while (m--) { scanf("%d", &o); if (o == 1) { scanf("%d%d%d", &x, &y, &p); work(x, y, p); } else { scanf("%d%d", &x, &y); //判断当前位置x是不是已经被修改过,判重 bool f = true; for (int i=0; i<c.size(); i++) if (c[i].idx == x) { c[i].v = y; f = false; break; } if (f) c.push_back(node(x, y)); } } } return 0; }