题目
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5332
分析
这道题目还是很繁琐的,看了题解写了好久才写出来
ST表+Two Pointers+后缀和维护+二分
对于给定的M,记l[i]=min{j|gcd(i,j)=M}, r[i]=max{j| M|a[j]}+1, 如果M不能整除a[i]那么l[i]=r[i]=i,这里l[i]和r[i]可以用two pointers线性解决
那么对于任意x属于[l[i],r[i]),gcd(i,x)=M
设s[i]=r[i]-l[i], 则s[i]表示gcd(i,x)=M的x的个数
将这个数列根据能不能整除M分段,第i段的起点为start[i]
每次查询L,R时,找到L和R所在的段,设为a和b
如果a=b,那么就要找[L,R)中l[x]>=R的第一个x,则答案为sigma(R-l[i]),L<=i<x,可以通过二分找到x,在维护l的后缀和求值
如果a!=b,那么对于(a,b)区间内的,答案为sigma(s[i]),可以通过维护s[i]的后缀和直接得到
对于a段内,类似a=b的情况,相当于找[L,start[a+1])中l[x]>=start[a+1]的第一个x
对于b段内,类似a=b的情况,相当于找[start[b],R)中l[x]>=R的第一个x
一开始想把不能整除的段都去掉,结果发现实现时情况太多,反而不如都留着,一块处理
附上题解原址http://wzimpha.sinaapp.com/archives/670
代码
/************************************************** * Problem: ZOJ 3800 * Author: clavichord93 * State: Accepted **************************************************/ #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> using namespace std; const int MAX_N = 100005; const int MAX_M = 50; const int MAX_D = 21; struct STTable { int n; int log2[MAX_N]; int dp[MAX_N][MAX_D]; void makeLog() { log2[0] = 0; for (int i = 1; i < MAX_N; i++) { log2[i] = log2[i - 1]; if ((1 << (log2[i] + 1)) == i) { log2[i]++; } } } void makeST(int *a, int size) { n = size; for (int i = 0; i < n; i++) { dp[i][0] = a[i]; } for (int j = 1; j < MAX_D; j++) { for (int i = 0; i < n - (1 << (j - 1)); i++) { dp[i][j] = __gcd(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]); } } } int getAns(int l, int r) { int st = log2[r - l + 1]; return __gcd(dp[l][st], dp[r - (1 << st) + 1][st]); } }; STTable stQuery; struct Solver { int l[MAX_N]; int r[MAX_N]; long long s[MAX_N]; int cntBlock; int start[MAX_N]; long long suffixSumL[MAX_N]; long long suffixSumSize[MAX_N]; void make(int *a, int n, int d) { memset(l, 0, sizeof(l)); memset(r, 0, sizeof(r)); memset(s, 0, sizeof(s)); memset(start, 0, sizeof(start)); memset(suffixSumL, 0, sizeof(suffixSumL)); memset(suffixSumSize, 0, sizeof(suffixSumSize)); cntBlock = 0; // Calculate l[] & r[] for (int i = 0, lastL = 0, lastR = 0; i < n; i++) { if (a[i] % d != 0) { l[i] = i; r[i] = i; lastL = i + 1; lastR = i + 1; } else { while (lastR < n && a[lastR] % d == 0) { lastR++; } lastL = max(lastL, i); while (lastL < lastR && stQuery.getAns(i, lastL) > d) { lastL++; } l[i] = lastL; r[i] = lastR; } } // Calculate s[] for (int i = n - 1; i >= 0; i--) { if (r[i] > l[i]) { s[i] = s[i + 1] + r[i] - l[i]; } else { s[i] = 0; } } // Get Groups cntBlock = 0; for (int i = 0; i < n; i++) { start[cntBlock++] = i; if (a[i] % d == 0) { while (i + 1 < n && a[i + 1] % d == 0) { i++; } } else { while (i + 1 < n && a[i + 1] % d != 0) { i++; } } } start[cntBlock] = n; // Calculate suffixSumL for (int i = n - 1; i >= 0; i--) { suffixSumL[i] = suffixSumL[i + 1] + l[i]; } // Calculate suffixSumSize for (int i = cntBlock - 1; i >= 0; i--) { suffixSumSize[i] = suffixSumSize[i + 1] + s[start[i]]; } //for (int i = 0; i < n; i++) { //printf("%d ", l[i]); //} //printf("\n"); //printf("\n"); } long long query(int queryL, int queryR, int d) { int a = upper_bound(start, start + cntBlock, queryL) - start - 1; int b = upper_bound(start, start + cntBlock, queryR - 1) - start - 1; //printf("A = %d, B = %d\n", a, b); if (a == b) { if (s[start[a]]) { int x = queryL; int y = lower_bound(l + queryL, l + start[a + 1], queryR) - l; //printf("X = %d, Y = %d\n", x, y); return (long long)queryR * (y - x) - (suffixSumL[x] - suffixSumL[y]); } return 0; } else { #ifdef debug printf("A = %d, B = %d\n", a, b); for (int i = 0; i < cntBlock; i++) { printf("%d ", start[i]); } printf("\n"); #endif long long ret = suffixSumSize[a + 1] - suffixSumSize[b]; #ifdef debug printf("%lld\n", ret); printf("Start = %d\n", start[a]); printf("L = %d, R = %d\n", l[start[a]], r[start[a]]); printf("S = %lld\n", s[start[a]]); #endif #ifdef debug printf("%lld\n", ret); #endif if (s[start[a]]) { int x = queryL; int y = lower_bound(l + queryL, l + start[a + 1], start[a + 1]) - l; ret += (long long)start[a + 1] * (y - x) - (suffixSumL[x] - suffixSumL[y]); } #ifdef debug printf("%lld\n", ret); #endif if (s[start[b]]) { int x = start[b]; int y = lower_bound(l + start[b], l + queryR, queryR) - l; ret += (long long)queryR * (y - x) - (suffixSumL[x] - suffixSumL[y]); } #ifdef debug printf("%lld\n", ret); #endif return ret; } } }; int n, m, q; int a[MAX_N]; int gs[MAX_N]; Solver solver[MAX_M]; int main() { #ifdef LOCAL_JUDGE freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); #endif stQuery.makeLog(); while (scanf("%d %d %d", &n, &m, &q) != EOF) { for (int i = 0; i < n; i++) { scanf("%d", &a[i]); } for (int i = 0; i < m; i++) { scanf("%d", &gs[i]); } sort(gs, gs + m); stQuery.makeST(a, n); for (int i = 0; i < m; i++) { solver[i].make(a, n, gs[i]); } for (int i = 0; i < q; i++) { int l, r, g; scanf("%d %d %d", &l, &r, &g); int t = lower_bound(gs, gs + m, g) - gs; printf("%lld\n", solver[t].query(l, r, g)); } } return 0; }
分析
线段树
对于给定的R,gcd(L,R)随L变小而变小,维护gcd(L,R)相等的段,那么段数不会超过a[R]的约数个
于是离线所有询问,枚举R,从左往右扫描,对每一个GS用一棵线段树维护G(L,R)的值
维护gcd(L,R)相等的段,在对应线段树上做区间+1,因为这一段中的i的gcd(i,R)都为该值
然后处理以R结尾的询问,做区间求和即可
感觉比上面的方法容易想也容易实现,效率甚至更好
参见http://blog.csdn.net/qian99/article/details/38827863
代码
/************************************************** * Problem: ZOJ 3800 * Author: clavichord93 * State: Accepted **************************************************/ #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int MAX_N = 100005; const int MAX_Q = 100005; const int MAX_M = 50; struct Event { int l, g, id; Event() {} Event(int _l, int _g, int _id) : l(_l), g(_g), id(_id) {} }; #define lch(t) (t << 1) #define rch(t) (t << 1 | 1) struct SegmentTree { int tag[MAX_N << 2]; long long sum[MAX_N << 2]; void clear(int n) { memset(tag, 0, sizeof(int) * n * 4); memset(sum, 0, sizeof(long long) * n * 4); } void pushdown(int t, int l, int r) { if (tag[t]) { int lt = lch(t); int rt = rch(t); int mid = (l + r) >> 1; sum[lt] += (long long)tag[t] * (mid - l + 1); tag[lt] += tag[t]; sum[rt] += (long long)tag[t] * (r - mid); tag[rt] += tag[t]; tag[t] = 0; } } void add(int t, int l, int r, int x, int y) { if (x <= l && r <= y) { tag[t]++; sum[t] += r - l + 1; } else { pushdown(t, l, r); int mid = (l + r) >> 1; if (x <= mid) { add(lch(t), l, mid, x, y); } if (y > mid) { add(rch(t), mid + 1, r, x, y); } sum[t] = sum[lch(t)] + sum[rch(t)]; } } long long getAns(int t, int l, int r, int x, int y) { if (x <= l && r <= y) { return sum[t]; } else { pushdown(t, l, r); int mid = (l + r) >> 1; long long ret = 0; if (x <= mid) { ret += getAns(lch(t), l, mid, x, y); } if (y > mid) { ret += getAns(rch(t), mid + 1, r, x, y); } return ret; } } }; #undef lch #undef rch int n, m, q; int a[MAX_N]; int gs[MAX_M]; int id[MAX_N]; long long ans[MAX_Q]; SegmentTree sgt[MAX_M]; int cntBlock; int _cntBlock; pair<int, int> block[MAX_N]; pair<int, int> _block[MAX_N]; vector<Event> queries[MAX_N]; int main() { #ifdef LOCAL_JUDGE freopen("in.txt", "r", stdin); //freopen("out.txt", "w", stdout); #endif while (scanf("%d %d %d", &n, &m, &q) != EOF) { for (int i = 0; i < n; i++) { queries[i].clear(); } for (int i = 0; i < m; i++) { sgt[i].clear(n); } memset(id, 0xff, sizeof(id)); for (int i = 0; i < n; i++) { scanf("%d", &a[i]); } for (int i = 0; i < m; i++) { scanf("%d", &gs[i]); id[gs[i]] = i; } for (int i = 0; i < q; i++) { int l, r, g; scanf("%d %d %d", &l, &r, &g); queries[r - 1].push_back(Event(l, g, i)); } cntBlock = 0; for (int i = 0; i < n; i++) { _cntBlock = 0; for (int j = 0; j < cntBlock; j++) { block[j].second = __gcd(block[j].second, a[i]); if (!j || block[j].second != block[j - 1].second) { _block[_cntBlock++] = block[j]; } } if (!_cntBlock || a[i] != _block[_cntBlock - 1].second) { _block[_cntBlock++] = make_pair(i, a[i]); } for (int j = 0; j < _cntBlock; j++) { block[j] = _block[j]; } cntBlock = _cntBlock; //for (int j = 0; j < cntBlock; j++) { //printf("%d %d\n", block[j].first, block[j].second); //} //printf("\n"); for (int j = 0; j < cntBlock; j++) { int t = id[block[j].second]; if (t != -1) { if (j == cntBlock - 1) { //cout << "ADD " << t << " " << block[cntBlock - 1].first << " " << i << endl; sgt[t].add(1, 0, n - 1, block[cntBlock - 1].first, i); } else { //cout << "ADD " << t << " " << block[j].first << " " << block[j + 1].first << endl; sgt[t].add(1, 0, n - 1, block[j].first, block[j + 1].first - 1); } } } int cntQuery = queries[i].size(); for (int j = 0; j < cntQuery; j++) { int t = id[queries[i][j].g]; //cout << "GET " << t << " " << queries[i][j].l << " " << i << endl; ans[queries[i][j].id] = sgt[t].getAns(1, 0, n - 1, queries[i][j].l, i); } //cout << i << endl; } for (int i = 0; i < q; i++) { printf("%lld\n", ans[i]); } } return 0; }