hdu 5317 RGCDQ(前缀和)
题目链接:hdu 5317
这题看数据量就知道需要先预处理,然后对每个询问都需要在 O(logn) 以下的复杂度求出,由数学规律可以推出 1 <= F(x) <= 7,所以对每组(L, R),只需要求出它们在 1~7 的范围内的数量,然后暴力求出 gcd 即可。因为符合递增,可以设一个结点 struct { v[8]; } 记录 1~7 的数量,结点间可以相加减,也就可以用前缀和的思想去做(其实我也是看了别人的题解才明白这种思想,一开始用二分不是超时就是 wa 了,不过我竟发现自己手写的二分比库函数 lower_bound 要快!而且至少快 7~8 倍以上!看来以后用二分都尽量自己手写好了 (ㄒoㄒ)~~ )
先附上用前缀和的思想的代码,加入了输入输出挂:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N = 1000006; 6 7 struct node { 8 int v[8]; 9 node() { memset(v,0,sizeof(v)); } 10 node operator + (const node &n2) const { 11 node add; 12 for(int i = 1; i <= 7; ++i) 13 add.v[i] = v[i] + n2.v[i]; 14 return add; 15 } 16 node operator - (const node &n2) const { 17 node sub; 18 for(int i = 1; i <= 7; ++i) 19 sub.v[i] = v[i] - n2.v[i]; 20 return sub; 21 } 22 node& operator += (const node &n2) { 23 *this = *this + n2; 24 return *this; 25 } 26 node& operator -= (const node &n2) { 27 return *this = *this - n2; 28 } 29 }; 30 31 int num[N]; 32 node _count[N]; 33 inline void init(int n = N - 3) { 34 for(int i = 2; i <= n; ++i) 35 if(!num[i]) 36 for(int j = i; j <= n; j += i) ++num[j]; 37 for(int i = 2; i <= n; ++i) { 38 node tmp; 39 ++tmp.v[num[i]]; 40 _count[i] = _count[i - 1] + tmp; 41 } 42 } 43 44 inline int gcd(int a, int b) { 45 return b == 0 ? a: gcd(b, a % b); 46 } 47 48 #include<cctype> 49 template <typename T> 50 inline void read(T &x) { 51 x = 0; 52 char ch = getchar(); 53 bool flag = 0; 54 while(!isdigit(ch) && ch != '-') ch = getchar(); 55 if(ch == '-') { 56 flag = 1; 57 ch = getchar(); 58 } 59 while(isdigit(ch)) { 60 x = x * 10 + (ch - '0'); 61 ch = getchar(); 62 } 63 if(flag) x = -x; 64 } 65 66 template <typename T> 67 inline void write(const T &x) { 68 if(x < 10) putchar(char(x + '0')); 69 else write(x / 10); 70 } 71 72 int main() { 73 int t,L,R; 74 init(); 75 read(t); 76 while(t--) { 77 read(L); read(R); 78 node p = _count[R] - _count[L - 1]; 79 int ans = 0; 80 for(int i = 1; i <= 7; ++i) { 81 if(!p.v[i]) continue; 82 --p.v[i]; 83 for(int j = i; j <= 7; ++j) 84 if(p.v[j]) ans = max(ans, gcd(i,j)); 85 ++p.v[i]; 86 } 87 write(ans); 88 puts(""); 89 } 90 return 0; 91 }
说到前缀和,就可以联想起高效动态维护前缀和的树状数组。没错,只要能求前缀和的数据结构,都能用树状数组去维护,它的适用范围不只是简单的 int,long long 或者 一维数组(二维树状数组去维护)等等。因此我定义成模板类:
1 #include<cstdio> 2 #include<cstring> 3 #include<cctype> 4 #include<algorithm> 5 using namespace std; 6 const int N = 1000006; 7 8 struct node { 9 int v[8]; 10 void clear() { memset(v,0,sizeof(v)); } 11 node() { clear(); } 12 node operator + (const node &n2) const { 13 node add; 14 for(int i = 1; i <= 7; ++i) 15 add.v[i] = v[i] + n2.v[i]; 16 return add; 17 } 18 node operator - (const node &n2) const { 19 node sub; 20 for(int i = 1; i <= 7; ++i) 21 sub.v[i] = v[i] - n2.v[i]; 22 return sub; 23 } 24 }; 25 26 #define lowbit(x) ((x)&-(x)) 27 template <typename T> 28 struct tree { 29 T c[N]; 30 int maxn; 31 void clear() { // 或者直接 memset(c, 0, sizeof(c)) 也可以; 32 for(int i = 0; i <= maxn; ++i) 33 c[i].clear(); 34 } 35 tree(int maxn = N - 3): maxn(maxn) { clear(); } 36 T sum(int x) const { 37 T res; 38 while(x) { 39 res = res + c[x]; 40 x -= lowbit(x); 41 } 42 return res; 43 } 44 void add(int x, T d) { 45 while(x <= maxn) { 46 c[x] = c[x] + d; 47 x += lowbit(x); 48 } 49 } 50 }; 51 52 tree<node> tr; 53 54 int num[N]; 55 void init(int n = N - 3) { 56 for(int i = 2; i <= n; ++i) 57 if(!num[i]) 58 for(int j = i; j <= n; j += i) ++num[j]; 59 for(int i = 2; i <= n; ++i) { 60 node tmp; 61 ++tmp.v[num[i]]; 62 tr.add(i, tmp); 63 } 64 } 65 66 inline int gcd(int a, int b) { 67 return b == 0 ? a: gcd(b, a % b); 68 } 69 70 int main() { 71 int t,L,R; 72 init(); 73 scanf("%d",&t); 74 while(t--) { 75 scanf("%d %d",&L,&R); 76 node p = tr.sum(R) - tr.sum(L - 1); 77 int ans = 0; 78 for(int i = 1; i <= 7; ++i) { 79 if(!p.v[i]) continue; 80 --p.v[i]; 81 for(int j = i; j <= 7; ++j) 82 if(p.v[j]) ans = max(ans, gcd(i, j)); 83 ++p.v[i]; 84 } 85 printf("%d\n",ans); 86 } 87 return 0; 88 }