【题解】[COCI2017-2018#2] ​​Garaža

考点:分治思想 + 线段树。 (区间问题常见思考方式

考虑区间答案 = 左区间答案 + 右区间答案 + 跨区间答案。

注意到每次加入一个数时 gcd 要么不变,要么缩小到原来的 1/2

所以本质上只用 log a_i 个不同取值

这样双指针扫描就可以通过本题。

#include <bits/stdc++.h> #define fi first #define se second #define pii pair<int, int> #define ll long long using namespace std; const int Maxn = 1e5 + 5; int n, q, a[Maxn]; inline int read() { int X = 0; bool flag = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') flag = 0; ch = getchar(); } while (ch >= '0' && ch <= '9') { X = (X << 1) + (X << 3) + ch - '0'; ch = getchar(); } if (flag) return X; return ~(X - 1); } struct node { //(place, val) -> place 指的是区间中的相对位置 //定义 pr 为从左到右 gcd 的答案 // su 为从右到左 gcd 的答案 // step 1. 合并两个区间答案 (双指针扫一遍) // step 2. 求出新的 pr 和 su 数组 (把 gcd 相同的区间合并在一起) //注意 gcd =1 的段也要算进去 vector<pii> pr, su; ll s; node() { pr.clear(); su.clear(); s = 0; } } t[Maxn << 2]; int gcd(int x, int y) { return y == 0 ? x : gcd(y, x % y); } inline vector<pii> hb(vector<pii> x, vector<pii> y, int tx) { if (x.empty()) { return y; } if (y.empty()) { return x; } //为何保证正序合并,这里要先翻转回来 if (tx) { reverse(x.begin(), x.end()); reverse(y.begin(), y.end()); } vector<pii> now; assert(now.size() == 0); int szx = x.size() - 1, szy = y.size() - 1; //前半部分直接粘贴 for (int i = 0; i <= szx; i++) { now.push_back(x[i]); } //如果和前一项合并没有变化 for (int i = 0; i <= szy; i++) { int sz = now.size() - 1; if (y[i].se % now[sz].se == 0) { now[sz].fi = y[i].fi + x[szx].fi; } else { now.push_back(make_pair(y[i].fi + x[szx].fi, gcd(y[i].se, now[sz].se))); } } if (tx) { reverse(now.begin(), now.end()); } return now; } inline node meg(node x, node y) { //关键代码 node now; now.s = x.s + y.s; now.pr = hb(x.pr, y.pr, 0); //这里 su 实际上是反着存储的 //这样 hb 的时候就可以直接正序合并 now.su = hb(y.su, x.su, 1); int szx = x.su.size() - 1, szy = y.pr.size() - 1; if (szx < 0 || szy < 0) return now; //双指针扫描 int j = 0; for (int i = 0; i <= szx; i++) { while (j <= szy && gcd(x.su[i].se, y.pr[j].se) > 1) { j++; } //夹逼出一个范围 int t = x.su[i].fi; if (i < szx) { t -= x.su[i + 1].fi; } if (j) { now.s += 1ll * t * y.pr[j - 1].fi; } } return now; } void fz(node &x, int y) { x.pr.clear(); x.su.clear(); x.pr.push_back(make_pair(1, y)); x.su.push_back(make_pair(1, y)); x.s = (y > 1); } void build(int p, int l, int r) { if (l == r) { fz(t[p], a[l]); return; } int mid = l + r >> 1; build(p << 1, l, mid); build(p << 1 | 1, mid + 1, r); t[p] = meg(t[p << 1], t[p << 1 | 1]); } void upd(int p, int l, int r, int x) { if (l == r) { fz(t[p], a[l]); return; } int mid = l + r >> 1; x <= mid ? upd(p << 1, l, mid, x) : upd(p << 1 | 1, mid + 1, r, x); t[p] = meg(t[p << 1], t[p << 1 | 1]); } inline node qry(int p, int l, int r, int ql, int qr) { if (ql <= l && r <= qr) { return t[p]; } int mid = l + r >> 1; if (ql <= mid && mid < qr) { return meg(qry(p << 1, l, mid, ql, qr), qry(p << 1 | 1, mid + 1, r, ql, qr)); } else if (qr <= mid) { return qry(p << 1, l, mid, ql, qr); } else { return qry(p << 1 | 1, mid + 1, r, ql, qr); } } int main() { scanf("%d%d", &n, &q); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); } build(1, 1, n); for (int i = 1; i <= q; i++) { int op, l, r; scanf("%d%d%d", &op, &l, &r); if (op == 1) { a[l] = r; upd(1, 1, n, l); } else { printf("%lld\n", qry(1, 1, n, l, r).s); } } }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530238.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(9)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示