(预处理+莫队算法)HDU - 5381 The sum of gcd
Posted tak_fate
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(预处理+莫队算法)HDU - 5381 The sum of gcd相关的知识,希望对你有一定的参考价值。
题意:
一个长度为n的数列,m次查询L到R之间所有连续子序列的gcd之和。
分析:
很明显的莫队算法。
很明显发现了gcd是单调递减的,并且最多存在32个的性质。
想了很久,脑补了许多种方法来拉伸L和R,但是都有漏洞。
实际上,这道题还是比较复杂的。。
在思考的过程中,我没有充分利用gcd的递减性质。
这题其实这题有共通之处,至少在我的做法上是这样的。
可以发现,在R向右拉伸的过程中,增加的和只是从L到R+1中的每一个后缀的和。
向左则为减,L的移动同理。
那么我们只要提前预处理每个位置的前缀所包含的所有数,以为成段会有相等的值,所以用pair存值和数量。
同理,后缀也要预处理。
那么我们就可以在将L和R拉伸时,用log的复杂度进行增减。
而预处理的复杂度仅为n*logn*logn。
查询的总复杂度为n*sqrt(n)*logn。
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cmath> 5 #include <cstring> 6 #include <set> 7 #include <vector> 8 #include <queue> 9 #include <map> 10 #include <list> 11 #include <bitset> 12 #include <string> 13 #include <cctype> 14 #include <cstdlib> 15 16 using namespace std; 17 18 typedef long long ll; 19 typedef unsigned long long ull; 20 #define inf (0x3f3f3f3f) 21 #define lnf (0x3f3f3f3f3f3f3f3f) 22 #define eps (1e-8) 23 #define fi first 24 #define se second 25 typedef pair<ll, ll> pll; 26 27 int sgn(double a) { 28 return a < -eps ? -1 : a < eps ? 0 : 1; 29 } 30 31 const int maxn = 50010; 32 struct Q { 33 int l, r, index; 34 }; 35 Q query[maxn]; 36 int t, n, q; 37 int a[maxn]; 38 int b[maxn]; 39 const int bk = 250; 40 long long res; 41 long long ans[maxn]; 42 int f[maxn][20]; 43 44 void rmq_init() { 45 for(int i = 1; i <= n; i++) 46 f[i][0] = a[i]; 47 int k = floor(log((double)n) / log(2.0)); 48 for(int j = 1; j <= k; j++) 49 for(int i = n; i >= 1; i--) { 50 if(i + (1 << (j - 1)) <= n) 51 f[i][j] = __gcd(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); 52 } 53 } 54 55 int rmq(int i, int j) { 56 int k = floor(log((double)(j - i + 1)) / log(2.0)); 57 return __gcd(f[i][k], f[j - (1 << k) + 1][k]); 58 } 59 60 bool cmp (Q a, Q b) { 61 if(a.l / bk == b.l / bk)return a.r < b.r; 62 else return a.l / bk < b.l / bk; 63 } 64 65 66 vector<pll> pre[maxn]; 67 vector<pll> suf[maxn]; 68 69 70 int search2(int zl, int l, int b) { 71 int left = l, right = n; 72 int w = l + 1; 73 while(left <= right) { 74 int mid = (left + right) >> 1; 75 int pp = rmq(zl, mid); 76 if(pp == b) { 77 left = mid + 1; 78 w = mid; 79 } else { 80 right = mid - 1; 81 } 82 } 83 return w; 84 } 85 86 int search3(int zl, int l, int b) { 87 int left = l, right = n; 88 int w = l + 1; 89 while(left <= right) { 90 int mid = (left + right) >> 1; 91 int pp = rmq(n - mid + 1, n - zl + 1); 92 if(pp == b) { 93 left = mid + 1; 94 w = mid; 95 } else { 96 right = mid - 1; 97 } 98 } 99 return w; 100 } 101 102 void Pfinit() { 103 for(int i = 1; i <= n; i++) { 104 pre[i].clear(); 105 for(int s = i - 1; s < n;) { 106 int d = search2(i, s + 1, rmq(i, s + 1)); 107 pre[i].push_back(make_pair(d - s, rmq(i, s + 1))); 108 s = d; 109 } 110 } 111 for(int i = 1; i <= n; i++) { 112 b[i] = a[n - i + 1]; 113 } 114 for(int i = 1; i <= n; i++) { 115 suf[n - i + 1].clear(); 116 for(int s = i - 1; s < n;) { 117 int d = search3(i, s + 1, rmq(n - s, n - i + 1)); 118 suf[n - i + 1].push_back(make_pair(d - s, rmq(n - s, n - i + 1))); 119 s = d; 120 } 121 } 122 123 } 124 125 126 void add_left( int s, int x, int v) { 127 int num = s - x + 1; 128 for(int i = 0; i < pre[x].size(); i++) { 129 if(pre[x][i].fi >= num) { 130 res += num * pre[x][i].se * v; 131 break; 132 } else { 133 res += pre[x][i].fi * pre[x][i].se * v; 134 num -= pre[x][i].fi; 135 } 136 } 137 } 138 139 140 void add_right(int s, int x, int v) { 141 int num = x - s + 1; 142 for(int i = 0; i < suf[x].size(); i++) { 143 if(suf[x][i].fi >= num) { 144 res += num * suf[x][i].se * v; 145 break; 146 } else { 147 res += suf[x][i].fi * suf[x][i].se * v; 148 num -= suf[x][i].fi; 149 } 150 } 151 } 152 153 int main() { 154 scanf("%d", &t); 155 while(t--) { 156 res = 0; 157 scanf("%d", &n); 158 for(int i = 1; i <= n; i++) { 159 scanf("%d", &a[i]); 160 } 161 rmq_init(); 162 Pfinit(); 163 scanf("%d", &q); 164 for(int i = 0; i < q; i++) { 165 scanf("%d%d", &query[i].l, &query[i].r); 166 query[i].index = i; 167 } 168 sort(query, query + q, cmp); 169 int pl = 1, pr = 0; 170 for (int i = 0; i < q; i++) { 171 int index = query[i].index; 172 if (pr < query[i].r) { 173 for (int j = pr + 1; j <= query[i].r; j++) { 174 add_right(pl, j, 1); 175 } 176 177 } else { 178 for (int j = pr; j > query[i].r; j--) { 179 add_right(pl, j, -1); 180 } 181 } 182 pr = query[i].r; 183 if (pl < query[i].l) { 184 for (int j = pl; j < query[i].l; j++) { 185 add_left(pr, j, -1); 186 } 187 } else { 188 for (int j = pl - 1; j >= query[i].l; j--) { 189 add_left(pr, j, 1); 190 } 191 } 192 pl = query[i].l; 193 ans[index] = res; 194 } 195 for(int i = 0; i < q; i++) { 196 printf("%lld\\n", ans[i]); 197 } 198 } 199 return 0; 200 }
以上是关于(预处理+莫队算法)HDU - 5381 The sum of gcd的主要内容,如果未能解决你的问题,请参考以下文章
hdu 5381 The sum of gcd 2015多校联合训练赛#8莫队算法
HDU - 5381 The sum of gcd(莫队/线段树区间合并)