从零开始学算法/C++/第二天
已经炉火纯青了,对于[https://sim.csp.thusaac.com/contest/32/problem/3](第32次ccf认证 第四题宝藏) 可不可以用线段树做呢?也许有些困难,哪天闲的没事可以用线段树做一下(每天都很忙:-(
今天做的是[https://www.acwing.com/problem/content/247/](AcWing 246 区间最大公约数)
struct node//这是线段树的结点
{
int l, r;
long long gys, sum;
}t[N << 2];
说实话这题很简单,不过还是有收获的->数组a[l]到a[n],设b[i]是a[i]的差分数组,那么a[l]~a[r]这个区间的公约数等于{gcd( a[l], gcd(b[l+1] ~ b[r]) )}
在具体实现中设函数node ffind(int p,int l,int r)返回的是b[l]~b[r]的最大公约数,那么a[l] = ffind( 1, 1, l).
于是
node a = ffind(1, 1, x);
if (x == y)cout << a.sum << endl;
else cout << gcd(a.sum, ffind(1, x + 1, y).gys) << endl;//x,y是要查询的a数组的最大公约数的区间
下面是该题代码
#include<iostream>
#include <algorithm>
#include <cstdlib>
using namespace std;
const int N = 5e5 + 1;
//身不慕生,宁比泰山之重
long long w[N];
int n, m;
long long x, y, d;
char ch;
long long gcd(long long a, long long b) { return b ? gcd(b, a % b) : abs(a); }
struct node
{
int l, r;
long long gys, sum;
}t[N << 2];
void build(int p, int l, int r)
{
t[p] = { l,r };
if (l == r) { t[p] = { l,l,w[l] - w[l - 1],w[l]-w[l-1]}; return; }
int mid = l + r >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
t[p].gys = gcd(t[p << 1].gys, t[p << 1 | 1].gys);
t[p].sum = t[p << 1].sum + t[p << 1 | 1].sum;
}
void jk(int p, int x, long long k)
{
if (t[p].l == t[p].r) { t[p].sum += k, t[p].gys += k; return; }
int mid = t[p].l + t[p].r >> 1;
if (x <= mid)jk(p << 1, x, k);
else jk(p << 1 | 1, x, k);
t[p].gys = gcd(t[p << 1].gys, t[p << 1 | 1].gys);
t[p].sum = t[p << 1].sum + t[p << 1 | 1].sum;
}
node ffind(int p, int l, int r)
{
if (l <= t[p].l && t[p].r <= r)return t[p];
int mid = t[p].l + t[p].r >> 1;
if (r <= mid)return ffind(p << 1, l, r);
else if (l > mid)return ffind(p << 1 | 1, l, r);
else
{
node a, b, c;
a = ffind(p << 1, l, r), b = ffind(p << 1 | 1, l, r);
c.gys = gcd(a.gys, b.gys);
c.sum = a.sum + b.sum;
return c;
}
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; ++i)cin >> w[i];
build(1, 1, n);
while (m--)
{
cin >> ch;
if (ch == 'C')
{
cin >> x >> y >> d;
if (x > y)swap(x, y);
jk(1, x, d);
if (y != n)jk(1, y + 1, -d);
}
else
{
cin >> x >> y;
if (x > y)swap(x, y);
node a = ffind(1, 1, x);
if (x == y)cout << a.sum << endl;
else cout << gcd(a.sum, ffind(1, x + 1, y).gys) << endl;
}
}
return 0;
}
写这道题时看到一个很好笑的题解,因为在查询时需要得到数组a的值,题解里面竟然每一次给a数组的区间[l,r]加 d 时都给a数组的[l,r]每一个值都加 d,
那么他写线段树的意义在哪😄
昨天定的任务没有完成,因为今天玩太久了。