这个题目比较巧妙。
f(i)求的是i的素数因子个数。
首先需要处理因子个数。这个方法很多。
其实对于一个i来说,由于i最大是1000000,然而当2*3*5*7.....*17这时是最大能在这个范围的。其他数的素数因子数目必然小于这个。也就是f(i)最大是7。
这样只需要查询L到R区间内f(i)分别为1, 2, 3, 4, 5, 6, 7的个数,然后暴力枚举gcd的最大值就可以了。
当时人比较二,区间求和直接上了线段树,然后MLE了,然后换成树状数组才AC。。。。
赛后经人提醒才发现,这个完全没有点修改操作,根本不需要使用上述工具。直接用sum数组保存前缀和就可以。然后sum[j] - sum[i-1]就是[i, j]区间的和了。。。
不管怎样第一次使用树状数组,还是附上代码。
代码:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <set> #include <map> #include <queue> #include <string> #include <algorithm> #define LL long long using namespace std; //GCD //求最大公约数 //O(logn) int gcd(int a, int b) { if (b == 0) return a; else return gcd(b, a%b); } const int maxN = 1000001; char f[maxN]; bool b[maxN]; int L, R; struct Val { int v[8]; Val() { memset(v, 0, sizeof(v)); } Val operator+(Val x) { Val ans; for (int i = 1; i <= 7; ++i) ans.v[i] = x.v[i] + v[i]; return ans; } Val operator-(Val x) { Val ans; for (int i = 1; i <= 7; ++i) ans.v[i] = v[i] - x.v[i]; return ans; } }; Val d[maxN]; int lowbit(int x) { return x&(-x); } void add(int pos,Val pls) { while(pos <= maxN)//x最大是N { d[pos] = d[pos] + pls; pos += lowbit(pos); } } Val sum(int to) { Val s; while(to > 0) { s = s + d[to]; to -= lowbit(to); } return s; } Val query(int from, int to) { return sum(to) - sum(from - 1); } void init() { int m = 1000000; memset(f,0,sizeof(f)); memset(b,0,sizeof(b)); for (int i = 2; i <= m; i++) { if (!b[i]) for(int j = i; j <= m; j = j+i) { b[j] = 1; f[j]++; } } for (int i = 2; i <= m; ++i) { Val tmp; tmp.v[f[i]] = 1; add(i, tmp); } } void work() { Val p = query(L, R); int ans = 0; for (int i = 1; i <= 7; ++i) { if (!p.v[i]) continue; p.v[i]--; for (int j = i; j <= 7; ++j) { if (!p.v[j]) continue; ans = max(ans, gcd(i, j)); } p.v[i]++; } printf("%d\n", ans); } int main() { //freopen("test.in", "r", stdin); init(); int T; scanf("%d", &T); for (int times = 0; times < T; ++times) { scanf("%d%d", &L, &R); work(); } return 0; }