【题解】[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 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
本文链接:https://www.cnblogs.com/cqbzly/p/17530238.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步